Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Annotations(compiler, assertOnChange)
.process(externs, root);
}
public static class PropogateConstantAnnotations
extends AbstractPostOrderCallback
implements CompilerPass {
private final AbstractCompiler compiler;
private final boolean assertOnChange;
public PropogateConstantAnnotations(
AbstractCompiler compiler, boolean forbidChanges) {
this.compiler = compiler;
this.assertOnChange = forbidChanges;
}
@Override
public void process(Node externs, Node root) {
new NodeTraversal(compiler, this).traverseRoots(externs, root);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Note: Constant properties annotations are not propagated.
if (n.getType() == Token.NAME) {
if (n.getString().isEmpty()) {
return;
}
JSDocInfo info = null;
// Find the JSDocInfo for a top level variable.
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
if ((info != null && info.isConstant()) &&
!n.getBooleanProp(Node.IS_CONSTANT_NAME)) {
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
if (assertOnChange) {
String name = n.getString();
throw new IllegalStateException(
"Unexpected const change.\n" +
" name: "+ name + "\n" +
" gramps:" + n.getParent().getParent().toStringTree());
}
// Even though the AST has changed (an annotation was added),
// the annotations are not compared so don't report the change.
// reportCodeChange("constant annotation");
}
}
}
}
/**
* Walk the AST tree and verify that constant names are used consistently.
*/
static class VerifyConstants extends AbstractPostOrderCallback
implements CompilerPass {
final private AbstractCompiler compiler;
final private boolean checkUserDeclarations;
VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) {
this.compiler = compiler;
this.checkUserDeclarations = checkUserDeclarations;
}
@Override
public void process(Node externs, Node root) {
Node externsAndJs = root.getParent();
Preconditions.checkState
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(externsAndJs != null);
Preconditions.checkState(externsAndJs.hasChild(externs));
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
private Map<String,Boolean> constantMap = Maps.newHashMap();
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.NAME) {
String name = n.getString();
if (n.getString().isEmpty()) {
return;
}
boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (checkUserDeclarations) {
boolean expectedConst = false;
if (NodeUtil.isConstantName(n)
|| compiler.getCodingConvention().isConstant(n.getString())) {
expectedConst = true;
} else {
expectedConst = false;
JSDocInfo info = null;
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
if (info != null && info.isConstant()) {
expectedConst = true;
} else {
expectedConst = false;
}
}
if (expectedConst) {
Preconditions.checkState(expectedConst == isConst,
"The name " + name + " is not annotated as constant.");
} else {
Preconditions.checkState(expectedConst == isConst,
"The name " + name + " should not be annotated as constant.");
}
}
Boolean value = constantMap.get(name);
if (value == null) {
constantMap.put(name, isConst);
} else {
Preconditions.checkState(value.booleanValue() == isConst,
"The name " + name + " is not consistently annotated as " +
"constant.");
}
}
}
}
/**
* Simplify the AST:
* - VAR declarations split, so they represent exactly one child
* declaration.
* - WHILEs are converted to FORs
* - FOR loop are initializers are moved out of the FOR structure
* - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are
* moved into a block.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> */
static class NormalizeStatements implements Callback {
private final AbstractCompiler compiler;
private final boolean assertOnChange;
NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) {
this.compiler = compiler;
this.assertOnChange = assertOnChange;
}
private void reportCodeChange(String changeDescription) {
if (assertOnChange) {
throw new IllegalStateException(
"Normalize constraints violated:\n" + changeDescription);
}
compiler.reportCodeChange();
}
// Only inspect the children of SCRIPTs, BLOCKs, as all these
// are the only legal place for VARs.
if (NodeUtil.isStatementBlock(n)) {
splitVarDeclarations(n);
}
if (n.getType() == Token.FUNCTION) {
moveNamedFunctions(n.getLastChild());
}
}
// TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are
// fixed.
/**
* Limit the number of special cases where LABELs need to be handled. Only
* BLOCK and loops are allowed to be labeled. Loop labels must remain in
* place as the named continues are not allowed for labeled blocks.
*/
private void normalizeLabels(Node n) {
Preconditions.checkArgument(n.getType() == Token.LABEL);
Node last = n.getLastChild();
switch (last.getType()) {
case Token.LABEL:
case Token.BLOCK:
case Token.FOR:
case Token.WHILE:
case Token.DO:
return;
default:
Node block = new Node(Token.BLOCK);
n.replaceChild(last, block);
block.addChildToFront(last);
reportCodeChange("LABEL normalization");
return;
}
}
/**
* Bring the initializers out of FOR loops. These need to be placed
* before any associated LABEL nodes. This needs to be done from the top
* level label first so this is called as a pre-order callback (from
* shouldTraverse).
*
* @param n The node to inspect.
* @param before The node to insert the initializer before.
* @param beforeParent The parent of the node before which the initializer
* will be inserted.
*/
private void extractForInitializer(
Node n, Node before, Node beforeParent) {
for (Node
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
Node insertBefore = (before == null) ? c : before;
Node insertBeforeParent = (before == null) ? n : beforeParent;
switch (c.getType()) {
case Token.LABEL:
extractForInitializer(c, insertBefore, insertBeforeParent);
break;
case Token.FOR:
if (!NodeUtil.isForIn(c)
&& c.getFirstChild().getType() != Token.EMPTY) {
Node init = c.getFirstChild();
c.replaceChild(init, new Node(Token.EMPTY));
Node newStatement;
// Only VAR statements, and expressions are allowed,
// but are handled differently.
if (init.getType() == Token.VAR) {
newStatement = init;
} else {
newStatement = NodeUtil.newExpr(init);
}
insertBeforeParent.addChildBefore(newStatement, insertBefore);
reportCodeChange("FOR initializer");
}
break;
}
}
}
/**
* Split a var node such as:
* var a, b;
* into individual statements:
* var a;
* var b;
* @param n The whose children we should inspect.
*/
private void splitVarDeclarations(Node n) {
for (Node next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
if (c.getType() == Token.VAR) {
if (assertOnChange && !c.hasChildren()) {
throw new IllegalStateException("Empty VAR node.");
}
while (c.getFirstChild() != c.getLastChild()) {
Node name = c.getFirstChild();
c.removeChild(name);
Node newVar = new Node(
Token.VAR, name, n.getLineno(), n.getCharno());
n.addChildBefore(newVar, c);
reportCodeChange("VAR with multiple children");
}
}
}
}
/**
* Move all the functions that are valid at the execution of the first
* statement of the function to the beginning of the function definition.
*/
private void moveNamedFunctions(Node functionBody) {
Preconditions.checkState(
functionBody.getParent
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>().getType() == Token.FUNCTION);
Node previous = null;
Node current = functionBody.getFirstChild();
// Skip any declarations at the beginning of the function body, they
// are already in the right place.
while (current != null && NodeUtil.isFunctionDeclaration(current)) {
previous = current;
current = current.getNext();
}
// Find any remaining declarations and move them.
Node insertAfter = previous;
while (current != null) {
// Save off the next node as the current node maybe removed.
Node next = current.getNext();
if (NodeUtil.isFunctionDeclaration(current)) {
// Remove the declaration from the body.
Preconditions.checkNotNull(previous);
functionBody.removeChildAfter(previous);
// Readd the function at the top of the function body (after any
// previous declarations).
insertAfter = addToFront(functionBody, current, insertAfter);
reportCodeChange("Move function declaration not at top of function");
} else {
// Update the previous only if the current node hasn't been moved.
previous = current;
}
current = next;
}
}
/**
* @param after The child node to insert the newChild after, or null if
* newChild should be added to the front of parent's child list.
* @return The inserted child node.
*/
private Node addToFront(Node parent, Node newChild, Node after) {
if (after == null) {
parent.addChildToFront(newChild);
} else {
parent.addChildAfter(newChild, after);
}
return newChild;
}
}
/**
* Remove duplicate VAR declarations.
*/
private void removeDuplicateDeclarations(Node root) {
Callback tickler = new ScopeTicklingCallback();
ScopeCreator scopeCreator = new SyntacticScopeCreator(
* which have already been split into separate declarations, so there
* is no need to handle that here, and "for (var a;;);", which has
* been moved out of the loop.
* The result of this is that in each case the parent node is replaced
* which is generally dangerous in a traversal but is fine here with
* the scope creator, as the next node of interest is the parent's
* next sibling.
*/
private void replaceVar
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>WithAssignment(Node n, Node parent, Node gramps) {
if (n.hasChildren()) {
// The * is being initialize, preserve the new value.
parent.removeChild(n);
// Convert "var name = value" to "name = value"
Node value = n.getFirstChild();
n.removeChild(value);
Node replacement = new Node(Token.ASSIGN, n, value);
gramps.replaceChild(parent, new Node(Token.EXPR_RESULT, replacement));
} else {
// It is an empty reference remove it.
if (NodeUtil.isStatementBlock(gramps)) {
gramps.removeChild(parent);
} else if (gramps.getType() == Token.FOR) {
// This is the "for (var a in b)..." case. We don't need to worry
// about initializers in "for (var a;;)..." as those are moved out
// as part of the other normalizations.
parent.removeChild(n);
gramps.replaceChild(parent, n);
} else {
Preconditions.checkState(gramps.getType() == Token.LABEL);
gramps.replaceChild(parent, new Node(Token.EMPTY));
}
}
reportCodeChange("Duplicate VAR declaration");
}
}
/**
* A simple class that causes scope to be created.
*/
private final class ScopeTicklingCallback
implements NodeTraversal.ScopedCallback {
@Override
public void enterScope(NodeTraversal t) {
// Cause the scope to be created, which will cause duplicate
// to be found.
t.getScope();
}
@Override
public void exitScope(NodeTraversal t) {
// Nothing to do.
}
@Override
public boolean shouldTraverse(
NodeTraversal nodeTraversal, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Nothing to do.
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> addVar(String name) {
int vIndex = itsVariableNames.get(name, -1);
if (vIndex != -1) {
// There's already a variable or parameter with this name.
if (vIndex >= varStart) {
Object v = itsConst.get(vIndex);
if (v != null)
return DUPLICATE_CONST;
else
return DUPLICATE_VAR;
} else
return DUPLICATE_PARAMETER;
}
int index = itsVariables.size();
itsVariables.add(name);
itsConst.add(null);
itsVariableNames.put(name, index);
return NO_DUPLICATE;
}
public final boolean addConst(String name) {
int vIndex = itsVariableNames.get(name, -1);
if (vIndex != -1) {
// There's already a variable or parameter with this name.
return false;
}
int index = itsVariables.size();
itsVariables.add(name);
itsConst.add(name);
itsVariableNames.put(name, index);
return true;
}
public final void removeParamOrVar(String name) {
int i = itsVariableNames.get(name, -1);
if (i != -1) {
itsVariables.remove(i);
itsVariableNames.remove(name);
ObjToIntMap.Iterator iter = itsVariableNames.newIterator();
for (iter.start(); !iter.done(); iter.next()) {
int v = iter.getValue();
if (v > i) {
iter.setValue(v - 1);
}
}
}
}
public final Object getCompilerData()
{
return compilerData;
}
public final void setCompilerData(Object data)
{
if (data == null) throw new IllegalArgumentException();
// Can only call once
if (compilerData != null) throw new IllegalStateException();
compilerData = data;
}
private int encodedSourceStart;
private int encodedSourceEnd;
private String sourceName;
private int baseLineno = -1;
private int endLineno = -1;
private ObjArray functions;
private ObjArray regexps;
// a list of the formal parameters and local variables
private ObjArray itsVariables
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> is relative only to the current
* run of the CodeConsumer and will be normalized
* later on by the SourceMap.
*
* @see SourceMap
*/
private static class Mapping {
Node node;
Position start;
Position end;
}
/**
* Starts the source mapping for the given
* node at the current position.
*/
@Override
void startSourceMapping(Node node) {
if (createSrcMap
&& node.getProp(Node.SOURCEFILE_PROP) != null
&& node.getLineno() > 0) {
int line = getCurrentLineIndex();
int index = getCurrentCharIndex();
// If the index is -1, we are not performing any mapping.
if (index >= 0) {
Mapping mapping = new Mapping();
mapping.node = node;
mapping.start = new Position(line, index);
mappings.push(mapping);
allMappings.add(mapping);
}
}
}
/**
* Finishes the source mapping for the given
* node at the current position.
*/
@Override
void endSourceMapping(Node node) {
if (createSrcMap
&& node.getProp(Node.SOURCEFILE_PROP) != null
&& node.getLineno() > 0) {
int line = getCurrentLineIndex();
int index = getCurrentCharIndex();
// If the index is -1, we are not performing any mapping.
if (index >= 0) {
Preconditions.checkState(
!mappings.empty(), "Mismatch in start and end of mapping");
Mapping mapping = mappings.pop();
mapping.end = new Position(line, index);
}
}
}
/**
* Generates the source map from the given code consumer,
* appending the information it saved to the SourceMap
* object given.
*/
@Override
void generateSourceMap(SourceMap map){
if (createSrcMap) {
for (Mapping mapping : allMappings) {
map.addMapping(mapping.node, mapping.start, mapping.end);
}
}
}
/**
* Reports to the code consumer that the given line has been cut at the
* given position (i.e. a \n has been inserted there). All mappings in
* the source maps after that position will be renormalized
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>:
case Token.SHEQ:
case Token.SHNE:
case Token.CASE:
Node left;
Node right;
if (operatorToken == Token.CASE) {
left = condition.getParent().getFirstChild(); // the switch condition
right = condition.getFirstChild();
} else {
left = condition.getFirstChild();
right = condition.getLastChild();
}
Node typeOfNode = null;
Node stringNode = null;
if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) {
typeOfNode = left;
stringNode = right;
} else if (right.getType() == Token.TYPEOF &&
left.getType() == Token.STRING) {
typeOfNode = right;
stringNode = left;
}
if (typeOfNode != null && stringNode != null) {
Node operandNode = typeOfNode.getFirstChild();
JSType operandType = getTypeIfRefinable(operandNode, blindScope);
if (operandType != null) {
boolean resultEqualsValue = operatorToken == Token.EQ ||
operatorToken == Token.SHEQ || operatorToken == Token.CASE;
if (!outcome) {
resultEqualsValue = !resultEqualsValue;
}
return caseTypeOf(operandNode, operandType, stringNode.getString(),
resultEqualsValue, blindScope);
}
}
}
switch (operatorToken) {
case Token.AND:
if (outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
}
case Token.OR:
if (!outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
}
case Token.EQ:
if (outcome) {
return caseEquality(condition, blindScope, EQ);
} else {
return caseEquality(condition, blindScope, NE);
}
case Token.NE:
if (outcome) {
return caseEquality
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(condition, blindScope, NE);
} else {
return caseEquality(condition, blindScope, EQ);
}
case Token.SHEQ:
if (outcome) {
return caseEquality(condition, blindScope, SHEQ);
} else {
return caseEquality(condition, blindScope, SHNE);
}
case Token.SHNE:
if (outcome) {
return caseEquality(condition, blindScope, SHNE);
} else {
return caseEquality(condition, blindScope, SHEQ);
}
case Token.NAME:
case Token.GETPROP:
return caseNameOrGetProp(condition, blindScope, outcome);
case Token.ASSIGN:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(),
firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild().getNext(), blindScope, outcome),
outcome);
case Token.NOT:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(), blindScope, !outcome);
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
if (outcome) {
return caseEquality(condition, blindScope, INEQ);
}
break;
case Token.INSTANCEOF:
return caseInstanceOf(
condition.getFirstChild(), condition.getLastChild(), blindScope,
outcome);
case Token.IN:
if (outcome && condition.getFirstChild().getType() == Token.STRING) {
return caseIn(condition.getLastChild(),
condition.getFirstChild().getString(), blindScope);
}
break;
case Token.CASE:
Node left =
condition.getParent().getFirstChild(); // the switch condition
Node right = condition.getFirstChild();
if (outcome) {
return caseEquality(left, right, blindScope, SHEQ);
} else {
return caseEquality(left, right, blindScope, SHNE);
}
}
return nextPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome);
}
private FlowScope caseEquality(Node condition, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
return caseEquality(condition.getFirstChild(), condition.getLastChild(),
blindScope, merging);
}
private FlowScope caseEquality
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(Node left, Node right, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
// left type
JSType leftType = getTypeIfRefinable(left, blindScope);
boolean leftIsRefineable;
if (leftType != null) {
leftIsRefineable = true;
} else {
leftIsRefineable = false;
leftType = left.getJSType();
}
// right type
JSType rightType = getTypeIfRefinable(right, blindScope);
boolean rightIsRefineable;
if (rightType != null) {
rightIsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.getJSType();
}
// merged types
TypePair merged = merging.apply(new TypePair(leftType, rightType));
// creating new scope
if (merged != null &&
((leftIsRefineable && merged.typeA != null) ||
(rightIsRefineable && merged.typeB != null))) {
FlowScope informed = blindScope.createChildFlowScope();
if (leftIsRefineable && merged.typeA != null) {
declareNameInScope(informed, left, merged.typeA);
}
if (rightIsRefineable && merged.typeB != null) {
declareNameInScope(informed, right, merged.typeB);
}
return informed;
}
return blindScope;
}
private FlowScope caseAndOrNotShortCircuiting(Node left, Node right,
FlowScope blindScope, boolean condition) {
// left type
JSType leftType = getTypeIfRefinable(left, blindScope);
boolean leftIsRefineable;
if (leftType != null) {
leftIsRefineable = true;
} else {
leftIsRefineable = false;
leftType = left.getJSType();
blindScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, condition);
}
// restricting left type
leftType = (leftType == null) ? null :
leftType.getRestrictedTypeGivenToBooleanOutcome(condition);
if (leftType == null) {
return firstPreciserScopeKnowingConditionOutcome(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
right, blindScope, condition);
}
// right type
JSType rightType = getTypeIfRefinable(right, blindScope);
boolean rightIsRefineable;
if (rightType != null) {
rightIsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.getJSType();
blindScope = firstPreciserScopeKnowingConditionOutcome(
right, blindScope, condition);
}
if (condition) {
rightType = (rightType == null) ? null :
rightType.getRestrictedTypeGivenToBooleanOutcome(condition);
// creating new scope
if ((leftType != null && leftIsRefineable) ||
(rightType != null && rightIsRefineable)) {
FlowScope informed = blindScope.createChildFlowScope();
if (leftIsRefineable && leftType != null) {
declareNameInScope(informed, left, leftType);
}
if (rightIsRefineable && rightType != null) {
declareNameInScope(informed, right, rightType);
}
return informed;
}
}
return blindScope;
}
private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right,
FlowScope blindScope, boolean condition) {
FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, !condition);
StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope);
if (leftVar == null) {
return blindScope;
}
FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, condition);
rightScope = firstPreciserScopeKnowingConditionOutcome(
right, rightScope, !condition);
StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope);
if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) {
return blindScope;
}
JSType type = leftVar.getType().getLeastSupertype(rightVar.getType());
FlowScope informed = blindScope.createChildFlowScope();
informed.inferSlotType(leftVar.getName(), type);
return informed;
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> }
private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope,
boolean outcome) {
JSType type = getTypeIfRefinable(name, blindScope);
if (type != null) {
JSType restrictedType =
type.getRestrictedTypeGivenToBooleanOutcome(outcome);
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, name, restrictedType);
return informed;
}
return blindScope;
}
private FlowScope caseTypeOf(Node node, JSType type, String value,
boolean resultEqualsValue, FlowScope blindScope) {
JSType restrictedType =
getRestrictedByTypeOfResult(type, value, resultEqualsValue);
if (restrictedType == null) {
return blindScope;
}
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, node, restrictedType);
return informed;
}
private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope,
boolean outcome) {
JSType leftType = getTypeIfRefinable(left, blindScope);
if (leftType == null) {
return blindScope;
}
JSType rightType = right.getJSType();
ObjectType targetType =
typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
if (rightType instanceof FunctionType) {
targetType = (FunctionType) rightType;
}
Visitor<JSType> visitor;
if (outcome) {
visitor = new RestrictByTrueInstanceOfResultVisitor(targetType);
} else {
visitor = new RestrictByFalseInstanceOfResultVisitor(targetType);
}
JSType restrictedLeftType = leftType.visit(visitor);
if (restrictedLeftType != null && !restrictedLeftType.equals(leftType)) {
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, left, restrictedLeftType);
return informed;
}
return blindScope;
}
/**
* Given 'property in object', ensures that the object has the property in the
* informed scope by defining it as a qualified name if the object type lacks
* the property and it's not in the blind scope.
* @param object The node of the right-
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>side of the in.
* @param propertyName The string of the left-side of the in.
*/
private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) {
JSType jsType = object.getJSType();
jsType = this.getRestrictedWithoutNull(jsType);
jsType = this.getRestrictedWithoutUndefined(jsType);
boolean hasProperty = false;
ObjectType objectType = ObjectType.cast(jsType);
if (objectType != null) {
hasProperty = objectType.hasProperty(propertyName);
}
if (!hasProperty) {
String qualifiedName = object.getQualifiedName();
if (qualifiedName != null) {
String propertyQualifiedName = qualifiedName + "." + propertyName;
if (blindScope.getSlot(propertyQualifiedName) == null) {
FlowScope informed = blindScope.createChildFlowScope();
JSType unknownType = typeRegistry.getNativeType(
JSTypeNative.UNKNOWN_TYPE);
informed.inferQualifiedSlot(
propertyQualifiedName, unknownType, unknownType);
return informed;
}
}
}
return blindScope;
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByTrueInstanceOfResultVisitor
extends RestrictByTrueTypeOfResultVisitor {
private final ObjectType target;
RestrictByTrueInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
protected JSType caseTopType(JSType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnknownType() {
if (target instanceof FunctionType) {
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
return funcTarget.getInstanceType();
}
}
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnionType(UnionType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
private JSType applyCommonRestriction(JSType type) {
if (target.isUnknownType()) {
return type;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
if (n.getType() == Token.EXPR_VOID) {
n.setType(Token.EXPR_RESULT);
reportChange();
}
// Remove unused properties to minimize differences between ASTs
// produced by the two parsers.
if (n.getType() == Token.FUNCTION) {
Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null);
}
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& n.getType() != Token.LABEL
&& n.getType() != Token.SWITCH) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n,c) &&
c.getType() != Token.BLOCK) {
Node newBlock = new Node(Token.BLOCK);
n.replaceChild(c, newBlock);
if (c.getType() != Token.EMPTY) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
extends NodeTraversal.AbstractPostOrderCallback {
private final AbstractCompiler compiler;
private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.compiler = compiler;
this.convention = compiler.getCodingConvention();
}
/**
*
* In the AST that Rhino gives us, it needs to make a distinction
* between jsdoc
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> on the object literal node and jsdoc on the object literal
* value. For example,
* <pre>
* var x = {
* / JSDOC /
* a: 'b',
* c: / JSDOC / 'd'
* };
* </pre>
*
* But in few narrow cases (in particular, function literals), it's
* a lot easier for us if the doc is attached to the value.
*/
public void visit(NodeTraversal t, Node n, Node parent) {
int nType = n.getType();
switch (nType) {
case Token.NAME:
case Token.STRING:
String nString = n.getString();
if (nType == Token.NAME &&
n.getParent().getType() == Token.CALL &&
"eval".equals(nString)) {
n.putBooleanProp(Node.DIRECT_EVAL, true);
}
if (convention.isConstant(nString)) {
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
break;
case Token.FUNCTION:
JSDocInfo fnInfo = n.getJSDocInfo();
if (fnInfo == null) {
// Look for the info on other nodes.
if (parent.getType() == Token.ASSIGN) {
// on ASSIGNs
fnInfo = parent.getJSDocInfo();
} else if (parent.getType() == Token.NAME) {
// on var NAME = function() { ... };
fnInfo = parent.getParent().getJSDocInfo();
}
}
// Compute which function parameters are optional and
// which are var_args.
Node args = n.getFirstChild().getNext();
for (Node arg = args.getFirstChild();
arg != null;
arg = arg.getNext()) {
String argName = arg.getString();
JSTypeExpression typeExpr = fnInfo == null ?
null : fnInfo.getParameterType(argName);
if (convention.isOptionalParameter(arg) ||
typeExpr != null && typeExpr.isOptionalArg()) {
arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true);
}
if (convention.isVarArgsParameter(arg) ||
typeExpr != null && typeExpr.isVarArgs())
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> {
arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true);
}
}
break;
case Token.OBJECTLIT:
if (n.getType() == Token.OBJECTLIT) {
for (Node key = n.getFirstChild();
key != null; key = key.getNext().getNext()) {
Node value = key.getNext();
if (key.getJSDocInfo() != null &&
key.getNext().getType() == Token.FUNCTION) {
value.setJSDocInfo(key.getJSDocInfo());
}
}
}
break;
}
// TODO(johnlenz): Determine if it is possible to simply use the javadoc
// everywhere rather than use IS_DISPATCHER.
/*
* Translate dispatcher info into the property expected node.
*/
if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) {
if (n.getType() == Token.ASSIGN) {
Node fnNode = n.getLastChild();
Preconditions.checkState(fnNode.getType() == Token.FUNCTION);
fnNode.putBooleanProp(Node.IS_DISPATCHER, true);
}
}
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> warnings should point.
* @param type The type being cast from.
* @param castType The type being cast to.
*/
void expectCanCast(NodeTraversal t, Node n, JSType type, JSType castType) {
castType = castType.restrictByNotNullOrUndefined();
type = type.restrictByNotNullOrUndefined();
if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) {
compiler.report(
JSError.make(t, n, INVALID_CAST,
castType.toString(), type.toString()));
registerMismatch(type, castType);
}
}
/**
* Expect that the given variable has not been declared with a type.
*
* @param sourceName The name of the source file we're in.
* @param n The node where warnings should point to.
* @param parent The parent of {@code n}.
* @param var The variable that we're checking.
* @param variableName The name of the variable.
* @param newType The type being applied to the variable. Mostly just here
* for the benefit of the warning.
*/
void expectUndeclaredVariable(String sourceName, Node n, Node parent, Var var,
String variableName, JSType newType) {
boolean allowDupe = false;
if (n.getType() == Token.GETPROP) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
}
JSType varType = var.getType();
// Only report duplicate declarations that have types. Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitial
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Scope and is a
// native type.
if (var.input == null) {
n.setJSType(varType);
if (parent.getType() == Token.VAR) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.getType() == Token.FUNCTION);
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().getType() == Token.EXPR_RESULT) ||
!newType.equals(varType)) {
compiler.report(
JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented.
*/
void expectAllInterfacePropertiesImplemented(FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
if (!instance.hasProperty(prop)) {
Node source = type.getSource();
Preconditions.checkNotNull(source);
String sourceName = (String) source.getProp(Node.SOURCENAME_PROP);
sourceName = sourceName == null ? "" : sourceName;
compiler.report(JSError.make(sourceName, source,
INTERFACE_METHOD_NOT_IMPLEMENTED,
prop, implemented.toString(), instance.toString()));
registerMismatch(instance, implemented);
}
}
}
}
}
/**
* Report a type mismatch
*/
private void mismatch(NodeTraversal t, Node n,
String msg, JSType found, JSType required) {
mismatch(t.getSourceName(), n, msg, found, required);
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
private void mismatch(NodeTraversal t, Node n,
String msg, JSType found, JSTypeNative required) {
mismatch(t, n, msg, found, getNativeType(required));
}
private void mismatch(String sourceName, Node n,
String msg, JSType found, JSType required) {
registerMismatch(found, required);
compiler.report(
JSError.make(sourceName, n, TYPE_MISMATCH_WARNING,
formatFoundRequired(msg, found, required)));
}
private void registerMismatch(JSType found, JSType required) {
// Don't register a mismatch for differences in null or undefined or if the
// code didn't downcast.
found = found.restrictByNotNullOrUndefined();
required = required.restrictByNotNullOrUndefined();
if (found.canAssignTo(required) || required.canAssignTo(found)) {
return;
}
mismatches.add(new TypeMismatch(found, required));
if (found instanceof FunctionType &&
required instanceof FunctionType) {
FunctionType fnTypeA = ((FunctionType) found);
FunctionType fnTypeB = ((FunctionType) required);
Iterator<Node> paramItA = fnTypeA.getParameters().iterator();
Iterator<Node> paramItB = fnTypeB.getParameters().iterator();
while (paramItA.hasNext() && paramItB.hasNext()) {
registerIfMismatch(paramItA.next().getJSType(),
paramItB.next().getJSType());
}
registerIfMismatch(fnTypeA.getReturnType(), fnTypeB.getReturnType());
}
}
private void registerIfMismatch(JSType found, JSType required) {
if (found != null && required != null &&
!found.canAssignTo(required)) {
registerMismatch(found, required);
}
}
/**
* Formats a found/required error message.
*/
private String formatFoundRequired(String description, JSType found,
JSType required) {
return MessageFormat.format(FOUND_REQUIRED, description, found, required);
}
/**
* Given a node, get a human-readable name for the type of that node so
* that will be easy for the programmer to find the original declaration.
*
* For example, if SubFoo's property "
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>bar" might have the human-readable
* name "Foo.prototype.bar".
*
* @param n The node.
* @param dereference If true, the type of the node will be dereferenced
* to an Object type, if possible.
*/
String getReadableJSTypeName(Node n, boolean dereference) {
// If we're analyzing a GETPROP, the property may be inherited by the
// prototype chain. So climb the prototype chain and find out where
// the property was originally defined.
if (n.getType() == Token.GETPROP) {
ObjectType objectType = getJSType(n.getFirstChild()).dereference();
if (objectType != null) {
String propName = n.getLastChild().getString();
while (objectType != null && !objectType.hasOwnProperty(propName)) {
objectType = objectType.getImplicitPrototype();
}
// Don't show complex function names or anonymous types.
// Instead, try to get a human-readable type name.
if (objectType != null &&
(objectType.getConstructor() != null ||
objectType.isFunctionPrototypeType())) {
return objectType.toString() + "." + propName;
}
}
}
JSType type = getJSType(n);
if (dereference) {
ObjectType dereferenced = type.dereference();
if (dereferenced != null) {
type = dereferenced;
}
}
String qualifiedName = n.getQualifiedName();
if (type.isFunctionPrototypeType() ||
(type.toObjectType() != null &&
type.toObjectType().getConstructor() != null)) {
return type.toString();
} else if (qualifiedName != null) {
return qualifiedName;
} else if (type instanceof FunctionType) {
// Don't show complex function names.
return "function";
} else {
return type.toString();
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(user): This branch indicates a compiler bug, not worthy of
// halting the compilation but we
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Finds all defines, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
List<Name> allDefines = Lists.newArrayList();
for (Name name : namespace.getNameIndex().values()) {
if (name.docInfo != null && name.docInfo.isDefine()) {
allDefines.add(name);
} else if (name.refs != null) {
for (Ref ref : name.refs) {
Node n = ref.node;
Node parent = ref.node.getParent();
JSDocInfo info = n.getJSDocInfo();
if (info == null &&
parent.getType() == Token.VAR && parent.hasOneChild()) {
info = parent.getJSDocInfo();
}
if (info != null && info.isDefine()) {
allDefines.add(name);
break;
}
}
}
}
CollectDefines pass = new CollectDefines(compiler, all
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.getType() == Token.ASSIGN) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.getType() == Token.NAME);
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.getType() == Token.CALL) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
// are not. But this would be overkill, expecially because
// the intended use of defines is with config_files, where
// all the defines are at the top of the bundle.
for (DefineInfo info : assignableDefines.values()) {
setDefineInfoNotAssignable(info, t);
}
assignableDefines.clear();
}
}
updateAssignAllowedStack(n, false);
}
/**
* Determines whether assignment to a define should be allowed
* in the subtree of the given node, and if not, records that fact.
*
* @param n The node whose subtree we're about to enter or exit.
* @param entering True if we're entering the subtree, false otherwise.
*/
private void updateAssignAllowedStack(Node n, boolean entering) {
switch (n.getType()) {
case Token.CASE:
case Token.FOR:
case Token.FUNCTION:
case Token.HOOK:
case Token.IF:
case Token.SWITCH:
case Token.WHILE:
if (entering) {
assignAllowed.push(0);
} else {
assignAllowed.remove();
}
break;
}
}
/**
* Determines whether assignment to a define should be allowed
* at the current point of the traversal.
*/
private boolean isAssignAllowed() {
return assignAllowed.element()
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> == 1;
}
/**
* Tracks the given define.
*
* @param t The current traversal, for context.
* @param name The full name for this define.
* @param value The value assigned to the define.
* @param valueParent The parent node of value.
* @return Whether we should remove this assignment from the parse tree.
*/
private boolean processDefineAssignment(NodeTraversal t,
String name, Node value, Node valueParent) {
if (value == null || !NodeUtil.isValidDefineValue(value,
allDefines.keySet())) {
compiler.report(
JSError.make(t, value, INVALID_DEFINE_INIT_ERROR, name));
} else if (!isAssignAllowed()) {
compiler.report(
JSError.make(t, valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name));
} else {
DefineInfo info = allDefines.get(name);
if (info == null) {
// First declaration of this define.
info = new DefineInfo(value, valueParent);
allDefines.put(name, info);
assignableDefines.put(name, info);
} else if (info.recordAssignment(value)) {
// The define was already initialized, but this is a safe
// re-assignment.
return true;
} else {
// The define was already initialized, and this is an unsafe
// re-assignment.
compiler.report(
JSError.make(t, valueParent, DEFINE_NOT_ASSIGNABLE_ERROR,
name, info.getReasonWhyNotAssignable()));
}
}
return false;
}
/**
* Gets the parent node of the value for any assignment to a Name.
* For example, in the assignment
* {@code var x = 3;}
* the parent would be the NAME node.
*/
private static Node getValueParent(Ref ref) {
// there are two types of declarations: VARs and ASSIGNs
return ref.node.getParent() != null &&
ref.node.getParent().getType() == Token.VAR ?
ref.node : ref.node.getParent();
}
/**
* Records the fact that because of the current node in the node traversal,
* the define can't ever be assigned again.
*
* @param
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> visit all nodes but not traverse into function
* bodies.
*/
public abstract static class AbstractShallowCallback implements Callback {
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
return parent == null || parent.getType() != Token.FUNCTION ||
n == parent.getFirstChild();
}
}
/**
* Abstract callback to visit all structure and statement nodes but doesn't
* traverse into functions or expressions.
*/
public abstract static class AbstractShallowStatementCallback
implements Callback {
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return parent == null || NodeUtil.isControlStructure(parent)
|| NodeUtil.isStatementBlock(parent);
}
}
/**
* Abstract callback to visit a pruned set of nodes.
*
*/
public abstract static class AbstractNodeTypePruningCallback
implements Callback {
private final Set<Integer> nodeTypes;
private final boolean include;
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) {
this(nodeTypes, true);
}
/**
* Creates an abstract pruned callback.
* @param nodeTypes the nodes to include/exclude in the traversal
* @param include whether to include or exclude the nodes in the traversal
*/
public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes,
boolean include) {
this.nodeTypes = nodeTypes;
this.include = include;
}
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
return include == nodeTypes.contains(n.getType());
}
}
/**
* Creates a node traversal using the specified callback interface.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb) {
this(compiler, cb, new SyntacticScopeCreator(compiler));
}
/**
* Creates a node traversal using the specified callback interface
* and the scope creator.
*/
public NodeTraversal(AbstractCompiler compiler, Callback cb,
ScopeCreator scopeCreator) {
this.callback = cb;
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
if (cb instanceof ScopedCallback) {
this.scopeCallback = (ScopedCallback) cb;
}
this.compiler = compiler;
this.sourceName = "";
this.scopeCreator = scopeCreator;
}
private void throwUnexpectedException(Exception unexpectedException) {
// If there's an unexpected exception, try to get the
// line number of the code that caused it.
String message = unexpectedException.getMessage();
// TODO(user): It is possible to get more information if curNode or
// its parent is missing. We still have the scope stack in which it is still
// very useful to find out at least which function caused the exception.
if (!sourceName.isEmpty()) {
message =
unexpectedException.getMessage() + "\n" +
formatNodeContext("Node", curNode) +
(curNode == null ?
"" :
formatNodeContext("Parent", curNode.getParent()));
}
compiler.throwInternalError(message, unexpectedException);
}
private String formatNodeContext(String label, Node n) {
if (n == null) {
return " " + label + ": NULL";
}
return " " + label + "(" + n.toString(false, false, false) + "): "
+ formatNodePosition(n);
}
/**
* Traverses a parse tree recursively.
*/
public void traverse(Node root) {
try {
sourceName = "";
curNode = root;
pushScope(root);
traverseBranch(root, null);
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException);
}
}
public void traverseRoots(Node ... roots) {
traverseRoots(Lists.newArrayList(roots));
}
public void traverseRoots(List<Node> roots) {
if (roots.isEmpty()) {
return;
}
try {
Node scopeRoot = roots.get(0).getParent();
Preconditions.checkState(scopeRoot != null);
sourceName = "";
curNode = scopeRoot;
pushScope(scopeRoot);
for (Node root : roots) {
Preconditions.checkState(root.getParent() == scopeRoot);
traverseBranch(root, scopeRoot);
}
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>);
}
}
private static final String MISSING_SOURCE = "[source unknown]";
private String formatNodePosition(Node n) {
if (n == null) {
return MISSING_SOURCE + "\n";
}
int lineNumber = n.getLineno();
int columnNumber = n.getCharno();
String src = compiler.getSourceLine(sourceName, lineNumber);
if (src == null) {
src = MISSING_SOURCE;
}
return sourceName + ":" + lineNumber + ":" + columnNumber + "\n"
+ src + "\n";
}
/**
* Traverses a parse tree recursively with a scope, starting with the given
* root. This should only be used in the global scope. Otherwise, use
* {@link #traverseAtScope}.
*/
void traverseWithScope(Node root, Scope s) {
Preconditions.checkState(s.isGlobal());
sourceName = "";
curNode = root;
pushScope(s);
traverseBranch(root, null);
popScope();
}
/**
* Traverses a parse tree recursively with a scope, starting at that scope's
* root.
*/
void traverseAtScope(Scope s) {
Node n = s.getRootNode();
if (n.getType() == Token.FUNCTION) {
// We need to do some extra magic to make sure that the scope doesn't
// get re-created when we dive into the function.
sourceName = getSourceName(n);
curNode = n;
pushScope(s);
Node args = n.getFirstChild().getNext();
Node body = args.getNext();
traverseBranch(args, n);
traverseBranch(body, n);
popScope();
} else {
traverseWithScope(n, s);
}
}
/**
* Traverses an inner node recursively with a refined scope. An inner node may
* be any node with a non {@code null} parent (i.e. all nodes except the
* root).
*
* @param node the node to traverse
* @param parent the node's parent, it may be not be {@code null}
* @param refinedScope the refined scope of the scope currently at the top of
* the scope stack or in trivial cases that very scope or {@
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>code null}
*/
protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) {
Preconditions.checkNotNull(parent);
if (refinedScope != null && getScope() != refinedScope) {
curNode = node;
pushScope(refinedScope);
traverseBranch(node, parent);
popScope();
} else {
traverseBranch(node, parent);
}
}
/**
* Gets the compiler.
*/
public Compiler getCompiler() {
// TODO(nicksantos): Remove this type cast. This is just temporary
// while refactoring.
return (Compiler) compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >=0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(sourceName);
}
/**
* Gets the current input module.
*/
public JSModule getModule() {
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
/**
* Traverses a branch.
*/
@SuppressWarnings("
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token.SCRIPT) {
sourceName = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getFirstChild().getType() == Token.NAME);
// the first child is the catch var and the third child
// is the code block
traverseBranch(n.getFirstChild(), n);
traverseBranch(n.getFirstChild().getNext().getNext(), n);
break;
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getType() == Token.FUNCTION);
final Node fnName = n.getFirstChild();
boolean anonymous = parent != null && NodeUtil.isFunctionAnonymous(n);
if (!anonymous) {
// Named functions are parent of the containing scope.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (anonymous) {
// Anonymous function names are parent of the contained scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.getType() == Token.BLOCK);
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>modules.length];
for (int i = 0; i < modules.length; i++) {
expected[i] = "";
for (CompilerInput input : modules[i].getInputs()) {
expected[i] += input.getSourceFile().getCode();
}
}
test(modules, expected, null, warning);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
*/
protected void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning) {
test(compiler, expected, error, warning, null);
}
/**
* Verifies that the compiler pass's JS output matches the expected output
* and (optionally) that an expected warning is issued. Or, if an error is
* expected, this method just verifies that the error is encountered.
*
* @param compiler A compiler that has been initialized via
* {@link Compiler#init}
* @param expected Expected output, or null if an error is expected
* @param error Expected error, or null if no error is expected
* @param warning Expected warning, or null if no warning is expected
* @param description The description of the expected warning,
* or null if no warning is expected or if the warning's description
* should not be examined
*/
private void test(Compiler compiler, String[] expected,
DiagnosticType error, DiagnosticType warning,
String description) {
RecentChange recentChange = new RecentChange();
compiler.addChangeHandler(recentChange);
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
Node externsRoot = root.getFirstChild();
Node mainRoot = root.getLastChild
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
assertEquals(
"Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()),
0, compiler.getErrorCount());
// Verify the symbol table.
ErrorManager symbolTableErrorManager =
new BlackHoleErrorManager(compiler);
Node expectedRoot = parseExpectedJs(expected);
expectedRoot.detachFromParent();
SymbolTable table = compiler.acquireSymbolTable();
table.verify(
new Node(Token.BLOCK, externsRoot.cloneTree(), expectedRoot),
mainRoot.getParent());
table.release();
JSError[] stErrors = symbolTableErrorManager.getErrors();
if (expectedSymbolTableError != null) {
assertEquals("There should be one error.", 1, stErrors.length);
assertEquals(expectedSymbolTableError, stErrors[0].getType());
} else {
assertEquals("Unexpected symbol table error(s): " +
Joiner.on("\n").join(stErrors),
0, stErrors.length);
}
if (warning == null) {
assertEquals(
"Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings),
0, aggregateWarningCount);
} else {
assertEquals("There should be one warning, repeated " + numRepetitions +
" time(s).", numRepetitions, aggregateWarningCount);
for (int i = 0; i < numRepetitions; ++i) {
JSError[] warnings = errorManagers[i].getWarnings();
JSError actual = warnings[0];
assertEquals(warning, actual.getType());
// Make sure that source information is always provided.
if (!allowSourcelessWarnings) {
assertTrue("Missing source file name in warning",
actual.sourceName != null && !actual.sourceName.isEmpty());
assertTrue("Missing line number in warning",
-1 != actual.lineNumber);
assertTrue("Missing char number in warning",
-1 != actual.getCharno());
}
if (description != null) {
assertEquals(description, actual.description);
}
}
}
if (normalizeEnabled) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRootClone, mainRootClone);
}
if (mainRootClone.checkTreeEqualsSilent(mainRoot)) {
assertFalse(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> = p2.error.sourceName;
if (source1 != null && source2 != null) {
int sourceCompare = source1.compareTo(source2);
if (sourceCompare != 0) {
return sourceCompare;
}
} else if (source1 == null && source2 != null) {
return P1_LT_P2;
} else if (source1 != null && source2 == null) {
return P1_GT_P2;
}
// lineno comparison
int lineno1 = p1.error.lineNumber;
int lineno2 = p2.error.lineNumber;
if (lineno1 != lineno2) {
return lineno1 - lineno2;
} else if (lineno1 < 0 && 0 <= lineno2) {
return P1_LT_P2;
} else if (0 <= lineno1 && lineno2 < 0) {
return P1_GT_P2;
}
// charno comparison
int charno1 = p1.error.getCharno();
int charno2 = p2.error.getCharno();
if (charno1 != charno2) {
return charno1 - charno2;
} else if (charno1 < 0 && 0 <= charno2) {
return P1_LT_P2;
} else if (0 <= charno1 && charno2 < 0) {
return P1_GT_P2;
}
// description
return p1.error.description.compareTo(p2.error.description);
}
}
static class ErrorWithLevel {
final JSError error;
final CheckLevel level;
ErrorWithLevel(JSError error, CheckLevel level) {
this.error = error;
this.level = level;
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>IN_EXTERNS_ERROR =
DiagnosticType.warning(
"JSC_NAME_REFERENCE_IN_EXTERNS",
"accessing name {0} in externs has no effect");
static final DiagnosticType INVALID_FUNCTION_DECL =
DiagnosticType.error("JSC_INVALID_FUNCTION_DECL",
"Syntax error: function declaration must have a name");
private CompilerInput synthesizedExternsInput = null;
private Node synthesizedExternsRoot = null;
private final AbstractCompiler compiler;
// Whether this is the post-processing sanity check.
private final boolean sanityCheck;
VarCheck(AbstractCompiler compiler) {
this(compiler, false);
}
VarCheck(AbstractCompiler compiler, boolean sanityCheck) {
this.compiler = compiler;
this.sanityCheck = sanityCheck;
}
/** {@inheritDoc} */
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck());
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
/** {@inheritDoc} */
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.getType() != Token.NAME) {
return;
}
if (NodeUtil.isLabelName(n)) {
return;
}
String varName = n.getString();
// Only a function can have an empty name.
if (varName.isEmpty()) {
Preconditions.checkState(NodeUtil.isFunction(parent));
// A function declaration with an empty name passes Rhino,
// but is supposed to be a syntax error according to the spec.
if (!NodeUtil.isAnonymousFunction(parent)) {
t.report(n, INVALID_FUNCTION_DECL);
}
return;
}
// Check that the var has been declared.
Scope scope = t.getScope();
Scope.Var var = scope.getVar(varName);
if (var == null) {
if (NodeUtil.isAnonymousFunction(parent)) {
// e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the
// current scope.
} else {
t.report(n, UNDEFINED_VAR_ERROR, varName);
if
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> (sanityCheck) {
throw new IllegalStateException("Unexpected variable " + varName);
} else {
// Create a new variable in a synthetic script. This will prevent
// subsequent compiler passes from crashing.
Node nameNode = Node.newString(Token.NAME, varName);
getSynthesizedExternsRoot().addChildToBack(
new Node(Token.VAR, nameNode));
scope.getGlobalScope().declare(varName, nameNode,
null, getSynthesizedExternsInput());
}
}
return;
}
CompilerInput currInput = t.getInput();
CompilerInput varInput = var.input;
if (currInput == varInput || currInput == null || varInput == null) {
// The variable was defined in the same file. This is fine.
return;
}
// Check module dependencies.
JSModule currModule = currInput.getModule();
JSModule varModule = varInput.getModule();
JSModuleGraph moduleGraph = compiler.getModuleGraph();
if (varModule != currModule && varModule != null && currModule != null) {
if (moduleGraph.dependsOn(currModule, varModule)) {
// The module dependency was properly declared.
} else {
if (!sanityCheck && scope.isGlobal()) {
if (moduleGraph.dependsOn(varModule, currModule)) {
// The variable reference violates a declared module dependency.
t.report(n, VIOLATED_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
} else {
// The variable reference is between two modules that have no
// dependency relationship. This should probably be considered an
// error, but just issue a warning for now.
t.report(n, MISSING_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
} else {
t.report(n, STRICT_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
}
}
}
/**
* A check for name references in the externs inputs. These used to prevent
* a variable from getting renamed, but no longer have any effect.
*/
private class NameRefInExternsCheck extends AbstractPostOrderCallback {
public void visit(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.NAME) {
switch (parent.getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.GETPROP:
case Token.LP:
// These are okay.
break;
default:
t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString());
break;
}
}
}
}
/** Lazily create a "new" externs input for undeclared variables. */
private CompilerInput getSynthesizedExternsInput() {
if (synthesizedExternsInput == null) {
synthesizedExternsInput =
compiler.newExternInput("{SyntheticVarsDeclar}");
}
return synthesizedExternsInput;
}
/** Lazily create a "new" externs root for undeclared variables. */
private Node getSynthesizedExternsRoot() {
if (synthesizedExternsRoot == null) {
CompilerInput synthesizedExterns = getSynthesizedExternsInput();
synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler);
}
return synthesizedExternsRoot;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>/*
* Copyright 2005 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A JavaScript module has a unique name, consists of a list of compiler inputs,
* and can depend on other modules.
*
*
*
*/
public class JSModule {
/** Module name */
private final String name;
/** Source code inputs */
private final List<CompilerInput> inputs = new ArrayList<CompilerInput>();
/** Modules that this module depends on */
private final List<JSModule> deps = new ArrayList<JSModule>();
/**
* Creates an instance.
*
* @param name A unique name for the module
*/
public JSModule(String name) {
this.name = name;
}
/** Gets the module name. */
public String getName() {
return name;
}
/** Adds a source file input to this module. */
public void add(JSSourceFile file) {
add(new CompilerInput(file));
}
/** Adds a source file input to this module. */
public void addFirst(JSSourceFile file) {
addFirst(new CompilerInput(file));
}
/** Adds a source code input to this module. */
public void add(CompilerInput input) {
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
inputs.add(input);
input.setModule(this);
}
/** Adds a source code input to this module. */
public void addFirst(CompilerInput input) {
inputs.add(0, input);
input.setModule(this);
}
/** Adds a source code input to this module directly after other. */
public void addAfter(CompilerInput input, CompilerInput other) {
Preconditions.checkState(inputs.contains(other));
inputs.add(inputs.indexOf(other), input);
input.setModule(this);
}
/** Adds a dependency on another module. */
public void addDependency(JSModule dep) {
Preconditions.checkState(dep != this);
deps.add(dep);
}
/** Removes all of the inputs from this module. */
public void removeAll() {
for (CompilerInput input : inputs) {
input.setModule(null);
}
inputs.clear();
}
/**
* Gets the list of modules that this module depends on.
*
* @return A list that may be empty but not null
*/
public List<JSModule> getDependencies() {
return deps;
}
/**
* Returns the transitive closure of dependencies starting from the
* dependencies of this module.
*/
public Set<JSModule> getAllDependencies() {
Set<JSModule> allDeps = Sets.newHashSet(deps);
List<JSModule> workList = Lists.newArrayList(deps);
while (workList.size() > 0) {
JSModule module = workList.remove(workList.size() - 1);
for (JSModule dep : module.getDependencies()) {
if (allDeps.add(dep)) {
workList.add(dep);
}
}
}
return allDeps;
}
/** Returns this module and all of its dependencies in one list. */
public Set<JSModule> getThisAndAllDependencies() {
Set<JSModule> deps = getAllDependencies();
deps.add(this);
return deps;
}
/**
* Gets this module's list of source code inputs.
*
* @return A list that may be empty but not null
*/
public List<CompilerInput> getInputs() {
return inputs;
}
/** Returns the input with the
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> given name or null if none. */
public CompilerInput getByName(String name) {
for (CompilerInput input : inputs) {
if (name.equals(input.getName())) {
return input;
}
}
return null;
}
/**
* Removes any input with the given name. Returns whether any were removed.
*/
public boolean removeByName(String name) {
boolean found = false;
Iterator<CompilerInput> iter = inputs.iterator();
while (iter.hasNext()) {
CompilerInput file = iter.next();
if (name.equals(file.getName())) {
iter.remove();
file.setModule(null);
found = true;
}
}
return found;
}
/** Returns the module name (primarily for debugging). */
@Override
public String toString() {
return name;
}
/**
* Removes any references to nodes of the AST. This method is needed to
* allow the ASTs to be garbage collected if the modules are kept around.
*/
public void clearAsts() {
for (CompilerInput input : inputs) {
input.clearAst();
}
}
/**
* Puts the JS files into a topologically sorted order by their dependencies.
*/
public void sortInputsByDeps(Compiler compiler) {
// Collect all symbols provided in these files.
final Map<String, CompilerInput> provides = Maps.newHashMap();
for (CompilerInput input : inputs) {
for (String provide : input.getProvides(compiler)) {
provides.put(provide, input);
}
}
// Put the files into topologically sorted order by their requires.
// NOTE: This will leave the list unchanged if the files are already
// topologically sorted. This is important to apps whose dependencies
// are incomplete.
List<CompilerInput> list = Lists.newArrayList();
Set<CompilerInput> set = Sets.newHashSet();
for (CompilerInput input : inputs) {
addInputAndDeps(input, provides, compiler, list, set,
Sets.<CompilerInput>newHashSet());
}
// Update the JSModule to this order.
Preconditions.checkState(inputs.size() == list.size());
inputs.clear();
inputs.addAll(list);
}
/**
*
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Returns the given collection of modules in topological order.
*
* Note that this will return the modules in the same order if they are
* already sorted, and in general, will only change the order as necessary to
* satisfy the ordering dependencies. This can be important for cases where
* the modules do not properly specify all dependencies.
*/
public static JSModule[] sortJsModules(Collection<JSModule> modules) {
List<JSModule> list = Lists.newArrayList();
Set<JSModule> set = Sets.newHashSet();
for (JSModule module : modules) {
addModuleAndDeps(module, list, set, Sets.<JSModule>newHashSet());
}
return list.toArray(new JSModule[list.size()]);
}
/**
* Adds the given input and its deps to the given list and set, if they are
* not already added, placing dependencies before dependants.
*/
private static void addInputAndDeps(
CompilerInput input, Map<String, CompilerInput> provides,
Compiler compiler, List<CompilerInput> list, Set<CompilerInput> set,
Set<CompilerInput> inProgress) {
if (!set.contains(input)) {
if (inProgress.contains(input)) {
throw new IllegalArgumentException(
"Circular dependency involving input: " + input.getName());
}
inProgress.add(input);
for (String require : input.getRequires(compiler)) {
if (provides.containsKey(require)) {
addInputAndDeps(provides.get(require), provides, compiler, list, set,
inProgress);
}
}
list.add(input);
set.add(input);
}
}
/**
* Adds the given module and its deps to the given list and set, if they are
* not already added, placing dependencies before dependants.
*/
private static void addModuleAndDeps(
JSModule module, List<JSModule> list, Set<JSModule> set,
Set<JSModule> inProgress) {
if (!set.contains(module)) {
if (inProgress.contains(module)) {
throw new IllegalArgumentException(
"Circular dependency involving module: " + module.getName());
}
inProgress.add(module);
for (JSModule dep : module.getDependencies()) {
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> of the preciser scope to the next link.
* If there is no next link, returns the blind scope.
*/
protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition,
FlowScope blindScope, boolean outcome) {
return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome) : blindScope;
}
/**
* Returns the type of a node in the given scope if the node corresponds to a
* name whose type is capable of being refined.
* @return The current type of the node if it can be refined, null otherwise.
*/
JSType getTypeIfRefinable(Node node, FlowScope scope) {
switch (node.getType()) {
case Token.NAME:
StaticSlot<JSType> nameVar = scope.getSlot(node.getString());
if (nameVar != null) {
JSType nameVarType = nameVar.getType();
if (nameVarType == null) {
nameVarType = node.getJSType();
}
return nameVarType;
}
return null;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
if (qualifiedName == null) {
return null;
}
StaticSlot<JSType> propVar = scope.getSlot(qualifiedName);
JSType propVarType = null;
if (propVar != null) {
propVarType = propVar.getType();
}
if (propVarType == null) {
propVarType = node.getJSType();
}
if (propVarType == null) {
propVarType = getNativeType(UNKNOWN_TYPE);
}
return propVarType;
}
return null;
}
/**
* Declares a refined type in {@code scope} for the name represented by
* {@code node}. It must be possible to refine the type of the given node in
* the given scope, as determined by {@link #getTypeIfRefinable}.
*/
protected void declareNameInScope(FlowScope scope, Node node, JSType type) {
switch (node.getType()) {
case Token.NAME:
scope.inferSlotType(node.getString(), type);
break;
case Token.GETPROP:
String
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> qualifiedName = node.getQualifiedName();
Preconditions.checkNotNull(qualifiedName);
JSType origType = node.getJSType();
origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
scope.inferQualifiedSlot(qualifiedName, origType, type);
break;
default:
throw new IllegalArgumentException("Node cannot be refined. \n" +
node.toStringTree());
}
}
/**
* @see #getRestrictedWithoutUndefined(JSType)
*/
private final Visitor<JSType> restrictUndefinedVisitor =
new Visitor<JSType>() {
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().equals(type)) {
return enumElementType;
} else {
return type;
}
}
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE);
}
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
public JSType caseFunctionType(FunctionType type) {
return type;
}
public JSType caseNullType() {
return getNativeType(NULL_TYPE);
}
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
public JSType caseObjectType(ObjectType type) {
return type;
}
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
public JSType caseUnionType(UnionType type) {
return type.getRestrictedUnion(getNativeType(VOID_TYPE));
}
public JSType caseUnknownType() {
return getNativeType(UNKNOWN_TYPE);
}
public JSType caseVoidType() {
return null;
}
};
/**
* @see #getRestrictedWithoutNull(JSType)
*/
private final Visitor<JSType> restrictNullVisitor =
new Visitor<JSType>() {
public JS
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>JSDocInfo inferJSDocInfo = null;
// These fields are used to calculate the percentage of expressions typed.
private int typedCount = 0;
private int nullCount = 0;
private int unknownCount = 0;
private boolean inExterns;
public TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry,
Scope topScope,
ScopeCreator scopeCreator,
CheckLevel reportMissingOverride,
CheckLevel reportUnknownTypes) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.reverseInterpreter = reverseInterpreter;
this.typeRegistry = typeRegistry;
this.topScope = topScope;
this.scopeCreator = scopeCreator;
this.reportMissingOverride = reportMissingOverride;
this.reportUnknownTypes = reportUnknownTypes;
this.inferJSDocInfo = new InferJSDocInfo(compiler);
}
public TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry,
CheckLevel reportMissingOverride,
CheckLevel reportUnknownTypes) {
this(compiler, reverseInterpreter, typeRegistry, null, null,
reportMissingOverride, reportUnknownTypes);
}
TypeCheck(AbstractCompiler compiler,
ReverseAbstractInterpreter reverseInterpreter,
JSTypeRegistry typeRegistry) {
this(compiler, reverseInterpreter, typeRegistry, null, null,
CheckLevel.WARNING, CheckLevel.OFF);
}
/** Turn on the missing property check. Returns this for easy chaining. */
TypeCheck reportMissingProperties(boolean report) {
reportMissingProperties = report;
return this;
}
/**
* Main entry point for this phase of processing. This follows the pattern for
* JSCompiler phases.
*
* @param externsRoot The root of the externs parse tree.
* @param jsRoot The root of the input parse tree to be checked.
*/
public void process(Node externsRoot, Node jsRoot) {
Preconditions.checkNotNull(scopeCreator);
Preconditions.checkNotNull(topScope);
Node externsAndJs = jsRoot.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(
externsRoot == null || externsAndJs.hasChild(externsRoot));
if (externsRoot
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> != null) {
check(externsRoot, true);
}
check(jsRoot, false);
potentialChecks.flush();
}
/** Main entry point of this phase for testing code. */
public Scope processForTesting(Node externsRoot, Node jsRoot) {
Preconditions.checkState(scopeCreator == null);
Preconditions.checkState(topScope == null);
Preconditions.checkState(jsRoot.getParent() != null);
Node externsAndJsRoot = jsRoot.getParent();
scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = scopeCreator.createScope(externsAndJsRoot, null);
TypeInferencePass inference = new TypeInferencePass(compiler,
reverseInterpreter, topScope, scopeCreator);
inference.process(externsRoot, jsRoot);
process(externsRoot, jsRoot);
return topScope;
}
public void check(Node node, boolean externs) {
Preconditions.checkNotNull(node);
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
inExterns = externs;
t.traverseWithScope(node, topScope);
if (externs) {
inferJSDocInfo.process(node, null);
} else {
inferJSDocInfo.process(null, node);
}
}
public boolean shouldTraverse(
NodeTraversal t, Node n, Node parent) {
JSDocInfo info;
switch (n.getType()) {
case Token.SCRIPT:
case Token.VAR:
// @notypecheck
info = n.getJSDocInfo();
if (info != null && info.isNoTypeCheck()) {
return false;
}
break;
case Token.FUNCTION:
// @notypecheck
info = n.getJSDocInfo();
info = (info == null) ? parent.getJSDocInfo() : info;
if (info != null && info.isNoTypeCheck()) {
return false;
}
// normal type checking
final TypeCheck outerThis = this;
final Scope outerScope = t.getScope();
final FunctionType functionType = (FunctionType) n.getJSType();
final String functionPrivateName = n.getFirstChild().getString();
if (functionPrivateName != null
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> && functionPrivateName.length() > 0 &&
outerScope.isDeclared(functionPrivateName, false) &&
// Ideally, we would want to check whether the type in the scope
// differs from the type being defined, but then the extern
// redeclarations of built-in types generates spurious warnings.
!(outerScope.getVar(
functionPrivateName).getType() instanceof FunctionType)) {
t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName);
}
// TODO(user): Only traverse the function's body. The function's
// name and arguments are traversed by the scope creator, and ideally
// should not be traversed by the type checker.
break;
}
return true;
}
/**
* This is the meat of the type checking. It is basically one big switch,
* with each case representing one type of parse tree node. The individual
* cases are usually pretty straightforward.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
*/
public void visit(NodeTraversal t, Node n, Node parent) {
JSType childType;
JSType leftType, rightType;
Node left, right;
// To be explicitly set to false if the node is not typeable.
boolean typeable = true;
switch (n.getType()) {
case Token.NAME:
typeable = visitName(t, n, parent);
break;
case Token.LP:
// If this is under a FUNCTION node, it is a parameter list and can be
// ignored here.
if (parent.getType() != Token.FUNCTION) {
ensureTyped(t, n, getJSType(n.getFirstChild()));
} else {
typeable = false;
}
break;
case Token.COMMA:
ensureTyped(t, n, getJSType(n.getLastChild()));
break;
case Token.TRUE:
case Token.FALSE:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.THIS:
ensureTyped(t, n, t.getScope().getTypeOfThis());
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> break;
case Token.REF_SPECIAL:
ensureTyped(t, n);
break;
case Token.GET_REF:
ensureTyped(t, n, getJSType(n.getFirstChild()));
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
if (n.getParent().getType() != Token.OBJECTLIT) {
ensureTyped(t, n, NUMBER_TYPE);
} else {
typeable = false;
}
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.STRING:
if (n.getParent().getType() != Token.OBJECTLIT) {
ensureTyped(t, n, STRING_TYPE);
} else {
typeable = false;
}
break;
case Token.REGEXP:
ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.getType() == Token.ASSIGN &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
break;
case Token.NEW:
visitNew(t, n);
typeable = true;
break;
case Token.CALL:
visitCall(t, n);
typeable = !NodeUtil.isExpressionNode(parent);
break;
case Token.RETURN:
visitReturn(t, n);
typeable = false;
break;
case Token.DEC:
case Token.INC:
left = n.getFirstChild();
validator.expectNumber(
t, left, getJSType(left), "increment/decrement");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.NOT:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.VOID:
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> ensureTyped(t, n, VOID_TYPE);
break;
case Token.TYPEOF:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.BITNOT:
childType = getJSType(n.getFirstChild());
if (!childType.matchesInt32Context()) {
t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
childType.toString());
}
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.POS:
case Token.NEG:
left = n.getFirstChild();
validator.expectNumber(t, left, getJSType(left), "sign operator");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.EQ:
case Token.NE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
TernaryValue result =
leftTypeRestricted.testForEquality(rightTypeRestricted);
if (result != TernaryValue.UNKNOWN) {
if (n.getType() == Token.NE) {
result = result.not();
}
t.report(n, DETERMINISTIC_TEST, leftType.toString(),
rightType.toString(), result.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.SHEQ:
case Token.SHNE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
if (!leftTypeRestricted.canTestForShallowEqualityWith(
rightTypeRestricted)) {
t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
rightType.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.LT:
case Token.LE:
case Token.GT:
case Token.GE:
left
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Type = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
if (rightType.isNumber()) {
validator.expectNumber(
t, n, leftType, "left side of numeric comparison");
} else if (leftType.isNumber()) {
validator.expectNumber(
t, n, rightType, "right side of numeric comparison");
} else if (leftType.matchesNumberContext() &&
rightType.matchesNumberContext()) {
// OK.
} else {
// Whether the comparison is numeric will be determined at runtime
// each time the expression is evaluated. Regardless, both operands
// should match a string context.
String message = "left side of comparison";
validator.expectString(t, n, leftType, message);
validator.expectNotVoid(
t, n, leftType, message, getNativeType(STRING_TYPE));
message = "right side of comparison";
validator.expectString(t, n, rightType, message);
validator.expectNotVoid(
t, n, rightType, message, getNativeType(STRING_TYPE));
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.IN:
left = n.getFirstChild();
right = n.getLastChild();
leftType = getJSType(left);
rightType = getJSType(right);
validator.expectObject(t, n, rightType, "'in' requires an object");
validator.expectString(t, left, leftType, "left side of 'in'");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.INSTANCEOF:
left = n.getFirstChild();
right = n.getLastChild();
leftType = getJSType(left);
rightType = getJSType(right).restrictByNotNullOrUndefined();
validator.expectAnyObject(
t, left, leftType, "deterministic instanceof yields false");
validator.expectActualObject(
t, right, rightType, "instanceof requires an object");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.ASSIGN:
visitAssign(t, n);
typeable = false;
break;
case Token.ASSIGN_LSH:
case Token.ASSIGN
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_SUB:
case Token.ASSIGN_ADD:
case Token.ASSIGN_MUL:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.DIV:
case Token.MOD:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.SUB:
case Token.ADD:
case Token.MUL:
visitBinaryOperator(n.getType(), t, n);
break;
case Token.DELPROP:
if (!isReference(n.getFirstChild())) {
t.report(n, BAD_DELETE);
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.CASE:
JSType switchType = getJSType(parent.getFirstChild());
JSType caseType = getJSType(n.getFirstChild());
validator.expectSwitchMatchesCase(t, n, switchType, caseType);
typeable = false;
break;
case Token.WITH: {
Node child = n.getFirstChild();
childType = getJSType(child);
validator.expectObject(
t, child, childType, "with requires an object");
typeable = false;
break;
}
case Token.FUNCTION:
visitFunction(t, n);
break;
// These nodes have no interesting type behavior.
case Token.LABEL:
case Token.SWITCH:
case Token.BREAK:
case Token.CATCH:
case Token.TRY:
case Token.SCRIPT:
case Token.EXPR_RESULT:
case Token.BLOCK:
case Token.EMPTY:
case Token.DEFAULT:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.THROW:
typeable = false;
break;
// These nodes require data flow analysis.
case Token.DO:
case Token.FOR:
case Token.IF:
case Token.WHILE:
typeable = false;
break;
// These nodes are typed during the type inference.
case Token
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.AND:
case Token.HOOK:
case Token.OBJECTLIT:
case Token.OR:
if (n.getJSType() != null) { // If we didn't run type inference.
ensureTyped(t, n);
} else {
// If this is an enum, then give that type to the objectlit as well.
if ((n.getType() == Token.OBJECTLIT)
&& (parent.getJSType() instanceof EnumType)) {
ensureTyped(t, n, parent.getJSType());
} else {
ensureTyped(t, n);
}
}
break;
default:
t.report(n, UNEXPECTED_TOKEN, Token.name(n.getType()));
ensureTyped(t, n);
break;
}
// Don't count externs since the user's code may not even use that part.
typeable = typeable && !inExterns;
if (typeable) {
doPercentTypedAccounting(t, n);
}
}
/**
* Counts the given node in the typed statistics.
* @param n a node that should be typed
*/
private void doPercentTypedAccounting(NodeTraversal t, Node n) {
JSType type = n.getJSType();
if (type == null) {
nullCount++;
} else if (type.isUnknownType()) {
if (reportUnknownTypes.isOn()) {
String unresolvedReference = getUnresolvedReference(type);
if (unresolvedReference != null) {
compiler.report(JSError.make(t, n, reportUnknownTypes,
UNRESOLVED_TYPE, unresolvedReference));
} else {
compiler.report(JSError.make(t, n, reportUnknownTypes,
UNKNOWN_EXPR_TYPE));
}
}
unknownCount++;
} else {
typedCount++;
}
}
/**
* Looks through the type to see if it contains an unresolved reference. This
* is often the reason that a type is unresolved, and it can occur because of
* a simple misspelling of a type name.
*/
private String getUnresolvedReference(JSType type) {
if (type.isNamedType()) {
NamedType namedType = (NamedType) type;
if (!namedType.is
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Resolved()) {
return namedType.getReferenceName();
}
} else if (type.isUnionType()) {
for (JSType alt : ((UnionType) type).getAlternates()) {
if (alt.isUnknownType()) {
String unresolvedReference = getUnresolvedReference(alt);
if (unresolvedReference != null) {
return unresolvedReference;
}
}
}
}
return null;
}
/**
* Visits an assignment <code>lvalue = rvalue</code>. If the
* <code>lvalue</code> is a prototype modification, we change the schema
* of the object type it is referring to.
* @param t the traversal
* @param assign the assign node
* (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant)
*/
private void visitAssign(NodeTraversal t, Node assign) {
JSDocInfo info = assign.getJSDocInfo();
Node lvalue = assign.getFirstChild();
Node rvalue = assign.getLastChild();
if (lvalue.getType() == Token.GETPROP) {
Node object = lvalue.getFirstChild();
JSType objectJsType = getJSType(object);
String property = lvalue.getLastChild().getString();
// the first name in this getprop refers to an interface
// we perform checks in addition to the ones below
if (object.getType() == Token.GETPROP) {
JSType jsType = getJSType(object.getFirstChild());
if (jsType.isInterface() &&
object.getLastChild().getString().equals("prototype")) {
visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue);
}
}
// /** @type ... */object.name = ...;
if (info != null && info.hasType()) {
visitAnnotatedAssignGetprop(t, assign,
info.getType().evaluate(t.getScope()), object,
property, rvalue);
return;
}
// /** @enum ... */object.name = ...;
if (info != null && info.hasEnumParameterType()) {
checkEnumInitializer(
t, rvalue, info.getEnumParameterType().evaluate(t.getScope()));
return;
}
// object.prototype = ...
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>;
if (property.equals("prototype")) {
if (objectJsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) objectJsType;
if (functionType.isConstructor()) {
JSType rvalueType = rvalue.getJSType();
validator.expectObject(t, rvalue, rvalueType,
OVERRIDING_PROTOTYPE_WITH_NON_OBJECT);
}
} else {
// TODO(user): might want to flag that
}
return;
}
// object.prototype.property = ...;
if (object.getType() == Token.GETPROP) {
Node object2 = object.getFirstChild();
String property2 = NodeUtil.getStringValue(object.getLastChild());
if ("prototype".equals(property2)) {
JSType jsType = object2.getJSType();
if (jsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) jsType;
if (functionType.isConstructor() || functionType.isInterface()) {
checkDeclaredPropertyInheritance(
t, assign, functionType, property, info, getJSType(rvalue));
}
} else {
// TODO(user): might want to flag that
}
return;
}
}
// object.property = ...;
ObjectType type = ObjectType.cast(
objectJsType.restrictByNotNullOrUndefined());
if (type != null) {
if (type.hasProperty(property) &&
!type.isPropertyTypeInferred(property) &&
!propertyIsImplicitCast(type, property)) {
validator.expectCanAssignToPropertyOf(
t, assign, getJSType(rvalue),
type.getPropertyType(property), object, property);
}
return;
}
} else if (lvalue.getType() == Token.NAME) {
// variable with inferred type case
JSType rvalueType = getJSType(assign.getLastChild());
Var var = t.getScope().getVar(lvalue.getString());
if (var != null) {
if (var.isTypeInferred()) {
return;
}
}
}
// fall through case
JSType leftType = getJSType(lvalue);
Node rightChild = assign.getLastChild();
JSType rightType = getJSType(rightChild);
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>,
propertyName, ctorType.getInstanceType().toString()));
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* interface.property2.property = ...;
* </pre>
*/
private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object,
String property, Node lvalue, Node rvalue) {
JSType rvalueType = getJSType(rvalue);
String abstractMethodName =
compiler.getCodingConvention().getAbstractMethodName();
if (!rvalueType.isOrdinaryFunction() &&
!(rvalue.isQualifiedName() &&
rvalue.getQualifiedName().equals(abstractMethodName))) {
compiler.report(JSError.make(t, object, INTERFACE_FUNCTION_MEMBERS_ONLY,
abstractMethodName));
}
if (assign.getLastChild().getType() == Token.FUNCTION
&& !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) {
compiler.report(JSError.make(t, object, INTERFACE_FUNCTION_NOT_EMPTY,
abstractMethodName));
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* object.property = ...;
* </pre>
* that have an {@code @type} annotation.
*/
private void visitAnnotatedAssignGetprop(NodeTraversal t,
Node assign, JSType type, Node object, String property, Node rvalue) {
// verifying that the rvalue has the correct type
validator.expectCanAssignToPropertyOf(t, assign, getJSType(rvalue), type,
object, property);
}
/**
* Visits a NAME node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
* @return whether the node is typeable or not
*/
boolean visitName(NodeTraversal t, Node n, Node parent) {
// At this stage, we need to determine whether this is a leaf
// node in an expression (which therefore needs to have a type
// assigned for it) versus some other decorative node that we
// can safely ignore.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Function names, arguments (children of LP nodes) and
// variable declarations are ignored.
// TODO(user): remove this short-circuiting in favor of a
// pre order traversal of the FUNCTION, CATCH, LP and VAR nodes.
int parentNodeType = parent.getType();
if (parentNodeType == Token.FUNCTION ||
parentNodeType == Token.CATCH ||
parentNodeType == Token.LP ||
parentNodeType == Token.VAR) {
return false;
}
JSType type = n.getJSType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
Var var = t.getScope().getVar(n.getString());
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
type = varType;
}
}
}
ensureTyped(t, n, type);
return true;
}
/**
* Visits a GETPROP node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of <code>n</code>
*/
private void visitGetProp(NodeTraversal t, Node n, Node parent) {
// GETPROP nodes have an assigned type on their node by the scope creator
// if this is an enum declaration. The only namespaced enum declarations
// that we allow are of the form object.name = ...;
if (n.getJSType() != null && parent.getType() == Token.ASSIGN) {
return;
}
// obj.prop or obj.method()
// Lots of types can appear on the left, a call to a void function can
// never be on the left. getPropertyType will decide what is acceptable
// and what isn't.
Node property = n.getLastChild();
Node objNode = n.getFirstChild();
JSType childType = getJSType(objNode);
// TODO(user): remove in favor of flagging every property access on
// non-object.
if (!validator.expectNotVoid(t, n, childType,
"undefined has no properties", getNativeType(OBJECT_TYPE))) {
ensureTyped(t, n);
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
return;
}
checkPropertyAccess(childType, property.getString(), t, n);
ensureTyped(t, n);
}
/**
* Make sure that the access of this property is ok.
*/
private void checkPropertyAccess(JSType childType, String propName,
NodeTraversal t, Node n) {
ObjectType objectType = childType.dereference();
if (objectType != null) {
JSType propType = getJSType(n);
if ((!objectType.hasProperty(propName) ||
objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) &&
propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) {
if (objectType instanceof EnumType) {
t.report(n, INEXISTENT_ENUM_ELEMENT, propName);
} else if (!objectType.isEmptyType() &&
reportMissingProperties && !isPropertyTest(n)) {
if (!typeRegistry.canPropertyBeDefined(objectType, propName)) {
t.report(n, INEXISTENT_PROPERTY, propName,
validator.getReadableJSTypeName(n.getFirstChild(), true));
}
}
}
} else {
// TODO(nicksantos): might want to flag the access on a non object when
// it's impossible to get a property from this type.
}
}
/**
* Determines whether this node is testing for the existence of a property.
* If true, we will not emit warnings about a missing property.
*
* @param getProp The GETPROP being tested.
*/
private boolean isPropertyTest(Node getProp) {
Node parent = getProp.getParent();
switch (parent.getType()) {
case Token.CALL:
return parent.getFirstChild() != getProp &&
compiler.getCodingConvention().isPropertyTestFunction(parent);
case Token.IF:
case Token.WHILE:
case Token.DO:
case Token.FOR:
return NodeUtil.getConditionExpression(parent) == getProp;
case Token.INSTANCEOF:
case Token.TYPEOF:
return true;
case Token.AND:
case Token.HOOK:
return parent.getFirstChild() == getProp;
}
return false;
}
/**
* Visits a GET
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>ELEM node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitGetElem(NodeTraversal t, Node n) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
validator.expectIndexMatch(t, n, getJSType(left), getJSType(right));
ensureTyped(t, n);
}
/**
* Visits a VAR node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitVar(NodeTraversal t, Node n) {
// TODO(nicksantos): Fix this so that the doc info always shows up
// on the NAME node. We probably want to wait for the parser
// merge to fix this.
JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null;
for (Node name : n.children()) {
Node value = name.getFirstChild();
// A null var would indicate a bug in the scope creation logic.
Var var = t.getScope().getVar(name.getString());
if (value != null) {
JSType valueType = getJSType(value);
JSType nameType = var.getType();
nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType;
JSDocInfo info = name.getJSDocInfo();
if (info == null) {
info = varInfo;
}
if (info != null && info.hasEnumParameterType()) {
// var.getType() can never be null, this would indicate a bug in the
// scope creation logic.
checkEnumInitializer(
t, value, info.getEnumParameterType().evaluate(t.getScope()));
} else if (var.isTypeInferred()) {
ensureTyped(t, name, valueType);
} else {
validator.expectCanAssignTo(
t, value, valueType, nameType, "initializing variable");
}
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
/**
* Visits a NEW node.
*/
private void visitNew(NodeTraversal t, Node n) {
Node constructor = n.getFirstChild();
FunctionType type = getFunctionType(constructor);
if (type != null && type.isConstructor()) {
visitParameterList(t, n, type);
ensureTyped(t, n, type.getInstanceType());
} else {
// TODO(user): add support for namespaced objects.
if (constructor.getType() != Token.GETPROP) {
// TODO(user): make the constructor node have lineno/charno
// and use constructor for a more precise error indication.
// It seems that GETPROP nodes are missing this information.
Node line;
if (constructor.getLineno() < 0 || constructor.getCharno() < 0) {
line = n;
} else {
line = constructor;
}
t.report(line, NOT_A_CONSTRUCTOR);
}
ensureTyped(t, n);
}
}
/**
* Visits a {@link Token#FUNCTION} node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitFunction(NodeTraversal t, Node n) {
JSDocInfo info = n.getJSDocInfo();
FunctionType functionType = (FunctionType) n.getJSType();
String functionPrivateName = n.getFirstChild().getString();
if (functionType.isInterface() || functionType.isConstructor()) {
FunctionType baseConstructor = functionType.
getPrototype().getImplicitPrototype().getConstructor();
if (baseConstructor != null &&
baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) &&
(baseConstructor.isConstructor() && functionType.isInterface() ||
baseConstructor.isInterface() && functionType.isConstructor())) {
compiler.report(
JSError.make(t, n, CONFLICTING_EXTENDED_TYPE, functionPrivateName));
}
for (JSType baseInterface : functionType.getImplementedInterfaces()) {
boolean badImplementedType = false;
ObjectType baseInterfaceObj = ObjectType.cast(baseInterface);
if (baseInterfaceObj
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> != null) {
FunctionType interfaceConstructor =
baseInterfaceObj.getConstructor();
if (interfaceConstructor != null &&
!interfaceConstructor.isInterface()) {
badImplementedType = true;
}
} else {
badImplementedType = true;
}
if (badImplementedType) {
t.report(n, BAD_IMPLEMENTED_TYPE, functionPrivateName);
}
}
if (functionType.isConstructor()) {
validator.expectAllInterfacePropertiesImplemented(functionType);
}
}
}
/**
* Visits a CALL node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitCall(NodeTraversal t, Node n) {
Node child = n.getFirstChild();
JSType childType = getJSType(child).restrictByNotNullOrUndefined();
if (!childType.canBeCalled()) {
t.report(n, NOT_CALLABLE, childType.toString());
ensureTyped(t, n);
return;
}
// A couple of types can be called as if they were functions.
// If it is a function type, then validate parameters.
if (childType instanceof FunctionType) {
FunctionType functionType = (FunctionType) childType;
// Non-native constructors should never be called directly.
if (functionType.isConstructor() &&
!functionType.isNativeObjectType()) {
t.report(n, CONSTRUCTOR_NOT_CALLABLE, childType.toString());
}
visitParameterList(t, n, functionType);
ensureTyped(t, n, functionType.getReturnType());
} else {
ensureTyped(t, n);
}
// TODO: Add something to check for calls of RegExp objects, which is not
// supported by IE. Either say something about the return type or warn
// about the non-portability of the call or both.
}
/**
* Visits the parameters of a CALL or a NEW node.
*/
private void visitParameterList(NodeTraversal t, Node call,
FunctionType functionType) {
Iterator<Node> arguments = call.children().iterator();
arguments.next(); // skip the function name
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Iterator<Node> parameters = functionType.getParameters().iterator();
int ordinal = 0;
while (arguments.hasNext() && parameters.hasNext()) {
Node parameter = parameters.next();
Node argument = arguments.next();
ordinal++;
validator.expectArgumentMatchesParameter(t, argument,
getJSType(argument), getJSType(parameter), call, ordinal);
}
int numArgs = call.getChildCount() - 1;
int minArgs = functionType.getMinArguments();
int maxArgs = functionType.getMaxArguments();
if (minArgs > numArgs || maxArgs < numArgs) {
t.getCompiler().report(
JSError.make(t, call, WRONG_ARGUMENT_COUNT,
validator.getReadableJSTypeName(call.getFirstChild(), false),
String.valueOf(numArgs), String.valueOf(minArgs),
maxArgs != Integer.MAX_VALUE ?
" and no more than " + maxArgs + " argument(s)" : ""));
}
}
/**
* Visits a RETURN node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitReturn(NodeTraversal t, Node n) {
Node function = t.getEnclosingFunction();
// This is a misplaced return, but the real JS will fail to compile,
// so let it go.
if (function == null) {
return;
}
JSType jsType = getJSType(function);
if (jsType instanceof FunctionType) {
FunctionType functionType = (FunctionType) jsType;
JSType returnType = functionType.getReturnType();
// if no return type is specified, undefined must be returned
// (it's a void function)
if (returnType == null) {
returnType = getNativeType(VOID_TYPE);
}
// fetching the returned value's type
Node valueNode = n.getFirstChild();
JSType actualReturnType;
if (valueNode == null) {
actualReturnType = getNativeType(VOID_TYPE);
valueNode = n;
} else {
actualReturnType = getJSType(valueNode);
}
// verifying
validator.expectCanAssignTo
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(t, valueNode, actualReturnType, returnType,
"inconsistent return type");
}
}
/**
* This function unifies the type checking involved in the core binary
* operators and the corresponding assignment operators. The representation
* used internally is such that common code can handle both kinds of
* operators easily.
*
* @param op The operator.
* @param t The traversal object, needed to report errors.
* @param n The node being checked.
*/
private void visitBinaryOperator(int op, NodeTraversal t, Node n) {
Node left = n.getFirstChild();
JSType leftType = getJSType(left);
Node right = n.getLastChild();
JSType rightType = getJSType(right);
switch (op) {
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.LSH:
case Token.RSH:
case Token.ASSIGN_URSH:
case Token.URSH:
if (!leftType.matchesInt32Context()) {
t.report(left, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), leftType.toString());
}
if (!rightType.matchesUint32Context()) {
t.report(right, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), rightType.toString());
}
break;
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_MUL:
case Token.ASSIGN_SUB:
case Token.DIV:
case Token.MOD:
case Token.MUL:
case Token.SUB:
validator.expectNumber(t, left, leftType, "left operand");
validator.expectNumber(t, right, rightType, "right operand");
break;
case Token.ASSIGN_BITAND:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITOR:
case Token.BITAND:
case Token.BITXOR:
case Token.BITOR:
validator.expectBitwiseable(t, left, leftType,
"bad left operand to bitwise operator");
validator.expectBitwiseable(t, right, rightType,
"bad right operand to bitwise operator");
break;
case Token.ASSIGN_ADD
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>:
case Token.ADD:
break;
default:
t.report(n, UNEXPECTED_TOKEN, Node.tokenToName(op));
}
ensureTyped(t, n);
}
/**
* <p>Checks the initializer of an enum. An enum can be initialized with an
* object literal whose values must be subtypes of the declared enum element
* type, or by copying another enum.</p>
*
* <p>In the case of an enum copy, we verify that the enum element type of the
* enum used for initialization is a subtype of the enum element type of
* the enum the value is being copied in.</p>
*
* <p>Examples:</p>
* <pre>var myEnum = {FOO: ..., BAR: ...};
* var myEnum = myOtherEnum;</pre>
*
* @param value the value used for initialization of the enum
* @param primitiveType The type of each element of the enum.
*/
private void checkEnumInitializer(
NodeTraversal t, Node value, JSType primitiveType) {
if (value.getType() == Token.OBJECTLIT) {
// re-using value as the value of the object literal and advancing twice
value = value.getFirstChild();
value = (value == null) ? null : value.getNext();
while (value != null) {
// the value's type must be assignable to the enum's primitive type
validator.expectCanAssignTo(t, value, getJSType(value), primitiveType,
"element type must match enum's type");
// advancing twice
value = value.getNext();
value = (value == null) ? null : value.getNext();
}
} else if (value.getJSType() instanceof EnumType) {
// TODO(user): Remove the instanceof check in favor
// of a type.isEnumType() predicate. Currently, not all enum types are
// implemented by the EnumClass, e.g. the unknown type and the any
// type. The types need to be defined by interfaces such that an
// implementation can implement multiple types interface.
EnumType valueEnumType = (EnumType) value.getJSType();
JSType valueEnumPrimitiveType =
valueEnumType.getElementsType().getPrimitiveType();
validator.expectCanAssignTo(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>t, value, valueEnumPrimitiveType,
primitiveType, "incompatible enum element types");
} else {
// The error condition is handled in TypedScopeCreator.
}
}
/**
* This predicate is used to determine if the node represents an expression
* that is a Reference according to JavaScript definitions.
*
* @param n The node being checked.
* @return true if the sub-tree n is a reference, false otherwise.
*/
private static boolean isReference(Node n) {
switch (n.getType()) {
case Token.GETELEM:
case Token.GETPROP:
case Token.NAME:
return true;
default:
return false;
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(nicksantos): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
/**
* Gets the type of the node or {@code null} if the node's type is not a
* function.
*/
private FunctionType getFunctionType(Node n) {
JSType type = getJSType(n).restrictByNotNullOrUndefined();
if (type.isUnknownType()) {
return typeRegistry.getNativeFunctionType(U2U_CONSTRUCTOR_TYPE);
} else if (type instanceof FunctionType) {
return (FunctionType) type;
} else {
return null;
}
}
// TODO(nicksantos): TypeCheck should never be attaching types to nodes.
// All types should be attached by TypeInference. This is not true today
// for legacy reasons. There are a number of places where TypeInference
// doesn't attach a type, as a signal to TypeCheck that it needs to check
// that node's type.
/**
* Ensure that the given node has a type. If it does not have one,
*
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> attach the UNKNOWN_TYPE.
*/
private void ensureTyped(NodeTraversal t, Node n) {
ensureTyped(t, n, getNativeType(UNKNOWN_TYPE));
}
private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) {
ensureTyped(t, n, getNativeType(type));
}
/**
* Enforces type casts, and ensures the node is typed.
*
* A cast in the way that we use it in JSDoc annotations never
* alters the generated code and therefore never can induce any runtime
* operation. What this means is that a 'cast' is really just a compile
* time constraint on the underlying value. In the future, we may add
* support for run-time casts for compiled tests.
*
* To ensure some shred of sanity, we enforce the notion that the
* type you are casting to may only meaningfully be a narrower type
* than the underlying declared type. We also invalidate optimizations
* on bad type casts.
*
* @param t The traversal object needed to report errors.
* @param n The node getting a type assigned to it.
* @param type The type to be assigned.
*/
private void ensureTyped(NodeTraversal t, Node n, JSType type) {
// Make sure FUNCTION nodes always get function type.
Preconditions.checkState(n.getType() != Token.FUNCTION ||
type instanceof FunctionType ||
type.isUnknownType());
JSDocInfo info = n.getJSDocInfo();
if (info != null) {
if (info.hasType()) {
JSType infoType = info.getType().evaluate(t.getScope());
validator.expectCanCast(t, n, infoType, type);
type = infoType;
}
if (info.isImplicitCast() && !inExterns) {
String propName = n.getType() == Token.GETPROP ?
n.getLastChild().getString() : "(missing)";
compiler.report(
JSError.make(t, n, ILLEGAL_IMPLICIT_CAST, propName));
}
}
if (n.getJSType() == null) {
n.setJSType(type);
}
}
/**
* Returns the percentage of
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> +
"\nCached : {0}\nActual : {1}");
static final DiagnosticType SCOPE_MISMATCH =
DiagnosticType.error(
"JSC_SCOPE_MISMATCH",
"Scope roots used with the symbol table do not match." +
"\nExpected : {0}\nActual : {1}");
private final AbstractCompiler compiler;
private final ScopeCreator scopeCreator;
// Mutex so that the symbol table may only be acquired by one pass
// at a time.
private boolean locked = false;
// Memoized data with the pass that has currently acquired the
// symbol table.
private MemoizedData cache = null;
SymbolTable(AbstractCompiler compiler) {
this.compiler = compiler;
compiler.addChangeHandler(this);
scopeCreator = new SyntacticScopeCreator(compiler);
}
synchronized void acquire() {
Preconditions.checkState(!locked, "SymbolTable already acquired");
locked = true;
}
synchronized void release() {
Preconditions.checkState(locked, "SymbolTable already released");
locked = false;
}
/**
* Returns the scope at the given node.
*/
@Override
public Scope createScope(Node n, Scope parent) {
// We may only ask for local blocks and the global (all scripts) block.
Preconditions.checkArgument(
(n.getType() == Token.BLOCK && n.getParent() == null) ||
n.getType() == Token.FUNCTION,
"May only create scopes for the global node and functions");
ensureCacheInitialized();
if (!cache.scopes.containsKey(n)) {
cache.scopes.put(n, scopeCreator.createScope(n, parent));
}
return cache.scopes.get(n);
}
/**
* Ensure that the memoization data structures have been initialized.
*/
private void ensureCacheInitialized() {
Preconditions.checkState(locked, "Unacquired symbol table");
if (cache == null) {
cache = new MemoizedData();
}
}
/**
* If the AST changes, and the symbol table has not been acquired, then
* all of our memoized data structures become stale. So delete them.
*/
@Override
public void reportChange() {
if (!locked) {
cache = null;
}
}
/**
* All the
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
Preconditions.checkState(expectedScopes.size() == actualScopes.size());
for (int i = 0; i < expectedScopes.size(); i++) {
Scope expectedScope = expectedScopes.get(i);
Scope actualScope = actualScopes.get(i);
if (!checkNodesMatch(expectedScope.getRootNode(),
actualScope.getRootNode())) {
compiler.report(
JSError.make(
SCOPE_MISMATCH,
expectedScope.getRootNode().toStringTree(),
actualScope.getRootNode().toStringTree()));
continue;
}
if (expectedScope.getVarCount() != actualScope.getVarCount()) {
compiler.report(
JSError.make(
VARIABLE_COUNT_MISMATCH,
Integer.toString(expectedScope.getVarCount()),
Integer.toString(actualScope.getVarCount())));
} else {
Iterator<Var> it = expectedScope.getVars();
while (it.hasNext()) {
Var var = it.next();
Scope.Var actualVar = actualScope.getVar(var.getName());
if (actualVar == null ||
expectedScope.getVar(var.getName()) != var) {
compiler.report(
JSError.make(MISSING_VARIABLE, var.getName()));
} else if (
!checkNodesMatch(
var.getNameNode(),
actualVar.getNameNode()) ||
!isNodeAttached(actualVar.getNameNode())) {
compiler.report(
JSError.make(MOVED_VARIABLE, var.getName()));
}
}
}
}
}
/**
* Check that the two nodes have the same relative position in the tree.
*/
private boolean checkNodesMatch(Node nodeA, Node nodeB) {
Node currentA = nodeA;
Node currentB = nodeB;
while (currentA != null && currentB != null) {
if (currentA.getType() != currentB.getType() ||
!currentA.isEquivalentTo(currentB)) {
return false;
}
currentA = currentA.getParent();
currentB = currentB.getParent();
}
return currentA == null && currentB == null;
}
private boolean isNodeAttached(Node node) {
// Make sure the cached var is still attached.
for (Node current = node;
current != null; current
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> = current.getParent()) {
if (current.getType() == Token.SCRIPT) {
return true;
}
}
return false;
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> except for
// their line numbers. Their line numbers will only be exposed for
// type name resolution warnings.
//
// TODO(nicksantos): Change rhino to put the whole Comment object
// on the Node.
private final Multimap<String, NodeWithJsDoc> nodesWithJsDoc =
LinkedHashMultimap.create();
private IRFactory(String sourceString,
String sourceName,
Config config,
ErrorReporter errorReporter) {
this.sourceString = sourceString;
this.sourceName = sourceName;
this.registry = config.registry;
this.config = config;
this.errorReporter = errorReporter;
this.transformDispatcher = new TransformDispatcher();
}
public static Node transformTree(AstRoot node,
String sourceString,
Config config,
ErrorReporter errorReporter) {
IRFactory irFactory = new IRFactory(sourceString, node.getSourceName(),
config, errorReporter);
Node irNode = irFactory.transform(node);
// @license text gets appended onto the fileLevelJsDocBuilder as found,
// and stored straight into the JSDocInfo for the root node.
Node.FileLevelJsDocBuilder fileLevelJsDocBuilder =
irNode.getJsDocBuilderForNode();
// fileOverviewInfo stores the last bit of fileoverview data we saw.
// We only permit one, so throwing away extras is fair.
// The fileOverviewInfo gets passed into parseJSDocInfo so that
// it can detect when multiple @fileoverviews exist in the same file.
JSDocInfo fileOverviewInfo = null;
if (node.getComments() != null) {
for (Comment comment : node.getComments()) {
if (comment.getCommentType() == JSDOC) {
JsDocInfoParser jsDocParser =
irFactory.createJsDocInfoParser(comment.getValue(),
comment.getLineno(), comment.getAbsolutePosition(),
fileLevelJsDocBuilder, fileOverviewInfo);
if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) {
fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo();
} else {
JSDocInfo info = jsDocParser.retrieveAndResetParsedJSDocInfo();
if (info != null) {
irFactory.attachJs
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Doc(comment, info);
}
}
}
}
// Only after we've seen all @fileoverview entries, attach the
// last one to the root node, and copy the found license strings
// to that node.
if (fileOverviewInfo != null) {
if ((irNode.getJSDocInfo() != null) &&
(irNode.getJSDocInfo().getLicense() != null)) {
fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense());
}
irNode.setJSDocInfo(fileOverviewInfo);
}
}
return irNode;
}
private Node transform(AstNode node) {
String jsDoc = node.getJsDoc();
NodeWithJsDoc nodeWithJsDoc = null;
if (jsDoc != null) {
nodeWithJsDoc = new NodeWithJsDoc();
nodesWithJsDoc.put(jsDoc, nodeWithJsDoc);
}
Node irNode = justTransform(node);
if (nodeWithJsDoc != null) {
nodeWithJsDoc.node = irNode;
}
// If we have a named function, set the position to that of the name.
if (irNode.getType() == Token.FUNCTION &&
irNode.getFirstChild().getLineno() != -1) {
irNode.setLineno(irNode.getFirstChild().getLineno());
irNode.setCharno(irNode.getFirstChild().getCharno());
} else {
if (irNode.getLineno() == -1) {
// If we didn't already set the line, then set it now. This avoids
// cases like ParenthesizedExpression where we just return a previous
// node, but don't want the new node to get its parent's line number.
int lineno = node.getLineno();
irNode.setLineno(lineno);
int charno = position2charno(node.getAbsolutePosition());
irNode.setCharno(charno);
}
}
return irNode;
}
/**
* Creates a JsDocInfoParser and parses the JsDoc string.
*
* Used both for handling individual JSDoc comments and for handling
* file-level JSDoc comments (@fileoverview and @license).
*
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>().getString());
}
@Override
Node processBlock(Block blockNode) {
return processGeneric(blockNode);
}
@Override
Node processBreakStatement(BreakStatement statementNode) {
Node node = new Node(Token.BREAK);
if (statementNode.getBreakLabel() != null) {
node.addChildToBack(transform(statementNode.getBreakLabel()));
}
return node;
}
@Override
Node processCatchClause(CatchClause clauseNode) {
AstNode catchVar = clauseNode.getVarName();
Node node = new Node(Token.CATCH, transform(catchVar));
if (clauseNode.getCatchCondition() != null) {
node.addChildToBack(transform(clauseNode.getCatchCondition()));
} else {
Node catchCondition = new Node(Token.EMPTY);
// Old Rhino used the position of the catchVar as the position
// for the (nonexistent) error being caught.
catchCondition.setLineno(catchVar.getLineno());
int clauseAbsolutePosition =
position2charno(catchVar.getAbsolutePosition());
catchCondition.setCharno(clauseAbsolutePosition);
node.addChildToBack(catchCondition);
}
node.addChildToBack(transform(clauseNode.getBody()));
return node;
}
@Override
Node processConditionalExpression(ConditionalExpression exprNode) {
return new Node(
Token.HOOK,
transform(exprNode.getTestExpression()),
transform(exprNode.getTrueExpression()),
transform(exprNode.getFalseExpression()));
}
@Override
Node processContinueStatement(ContinueStatement statementNode) {
Node node = new Node(Token.CONTINUE);
if (statementNode.getLabel() != null) {
node.addChildToBack(transform(statementNode.getLabel()));
}
return node;
}
@Override
Node processDoLoop(DoLoop loopNode) {
return new Node(
Token.DO,
transform(loopNode.getBody()),
transform(loopNode.getCondition()));
}
@Override
Node processElementGet(ElementGet getNode) {
return new Node(
Token.GETELEM,
transform(getNode.getTarget()),
transform(getNode.getElement()));
}
@Override
Node processEmptyExpression(EmptyExpression exprNode) {
Node node = new Node
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(Token.EMPTY);
return node;
}
@Override
Node processExpressionStatement(ExpressionStatement statementNode) {
Node node = new Node(transformTokenType(statementNode.getType()));
node.addChildToBack(transform(statementNode.getExpression()));
return node;
}
@Override
Node processForInLoop(ForInLoop loopNode) {
return new Node(
Token.FOR,
transform(loopNode.getIterator()),
transform(loopNode.getIteratedObject()),
transform(loopNode.getBody()));
}
@Override
Node processForLoop(ForLoop loopNode) {
Node node = new Node(
Token.FOR,
transform(loopNode.getInitializer()),
transform(loopNode.getCondition()),
transform(loopNode.getIncrement()));
node.addChildToBack(transform(loopNode.getBody()));
return node;
}
@Override
Node processFunctionCall(FunctionCall callNode) {
Node node = new Node(transformTokenType(callNode.getType()),
transform(callNode.getTarget()));
for (AstNode child : callNode.getArguments()) {
node.addChildToBack(transform(child));
}
int leftParamPos = callNode.getAbsolutePosition() + callNode.getLp();
node.setLineno(callNode.getLineno());
node.setCharno(position2charno(leftParamPos));
return node;
}
@Override
Node processFunctionNode(FunctionNode functionNode) {
Name name = functionNode.getFunctionName();
Boolean isUnnamedFunction = false;
if (name == null) {
name = new Name();
name.setIdentifier("");
isUnnamedFunction = true;
}
Node node = new com.google.javascript.rhino.FunctionNode(
name.getIdentifier());
node.putProp(Node.SOURCENAME_PROP, functionNode.getSourceName());
Node newName = transform(name);
if (isUnnamedFunction) {
// Old Rhino tagged the empty name node with the line number of the
// declaration.
newName.setLineno(functionNode.getLineno());
// TODO(user) Mark line number of paren correctly.
// Same problem as below - the left paren might not be on the
// same line as the function keyword.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
int lpColumn = functionNode.getAbsolutePosition() +
functionNode.getLp();
newName.setCharno(position2charno(lpColumn));
}
node.addChildToBack(newName);
Node lp = new Node(Token.LP);
// The left paren's complicated because it's not represented by an
// AstNode, so there's nothing that has the actual line number that it
// appeared on. We know the paren has to appear on the same line as the
// function name (or else a semicolon will be inserted.) If there's no
// function name, assume the paren was on the same line as the function.
// TODO(user): Mark line number of paren correctly.
Name fnName = functionNode.getFunctionName();
if (fnName != null) {
lp.setLineno(fnName.getLineno());
} else {
lp.setLineno(functionNode.getLineno());
}
int lparenCharno = functionNode.getLp() +
functionNode.getAbsolutePosition();
lp.setCharno(position2charno(lparenCharno));
for (AstNode param : functionNode.getParams()) {
lp.addChildToBack(transform(param));
}
node.addChildToBack(lp);
Node bodyNode = transform(functionNode.getBody());
parseDirectives(bodyNode);
node.addChildToBack(bodyNode);
return node;
}
@Override
Node processIfStatement(IfStatement statementNode) {
Node node = new Node(Token.IF);
node.addChildToBack(transform(statementNode.getCondition()));
node.addChildToBack(transform(statementNode.getThenPart()));
if (statementNode.getElsePart() != null) {
node.addChildToBack(transform(statementNode.getElsePart()));
}
return node;
}
@Override
Node processInfixExpression(InfixExpression exprNode) {
Node n = new Node(
transformTokenType(exprNode.getType()),
transform(exprNode.getLeft()),
transform(exprNode.getRight()));
// Set the line number here so we can fine-tune it in ways transform
// doesn't do.
n.setLineno(exprNode.getLineno());
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
return node;
}
@Override
Node processPropertyGet(PropertyGet getNode) {
return new Node(
Token.GETPROP,
transform(getNode.getTarget()),
transformAsString(getNode.getProperty()));
}
@Override
Node processRegExpLiteral(RegExpLiteral literalNode) {
Node literalStringNode = Node.newString(literalNode.getValue());
// assume it's on the same line.
literalStringNode.setLineno(literalNode.getLineno());
Node node = new Node(Token.REGEXP, literalStringNode);
String flags = literalNode.getFlags();
if (flags != null && !flags.isEmpty()) {
Node flagsNode = Node.newString(flags);
// Assume the flags are on the same line as the literal node.
flagsNode.setLineno(literalNode.getLineno());
node.addChildToBack(flagsNode);
}
return node;
}
@Override
Node processReturnStatement(ReturnStatement statementNode) {
Node node = new Node(Token.RETURN);
if (statementNode.getReturnValue() != null) {
node.addChildToBack(transform(statementNode.getReturnValue()));
}
return node;
}
@Override
Node processScope(Scope scopeNode) {
return processGeneric(scopeNode);
}
@Override
Node processStringLiteral(StringLiteral literalNode) {
Node n = Node.newString(literalNode.getValue());
return n;
}
@Override
Node processSwitchCase(SwitchCase caseNode) {
Node node;
if (caseNode.isDefault()) {
node = new Node(Token.DEFAULT);
} else {
AstNode expr = caseNode.getExpression();
node = new Node(Token.CASE, transform(expr));
}
Node block = new Node(Token.BLOCK);
block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
block.setLineno(caseNode.getLineno());
block.setCharno(position2charno(caseNode.getAbsolutePosition()));
if (caseNode.getStatements() != null) {
for (AstNode child : caseNode.getStatements()) {
block.addChildToBack(transform(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>child));
}
}
node.addChildToBack(block);
return node;
}
@Override
Node processSwitchStatement(SwitchStatement statementNode) {
Node node = new Node(Token.SWITCH,
transform(statementNode.getExpression()));
for (AstNode child : statementNode.getCases()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processThrowStatement(ThrowStatement statementNode) {
return new Node(Token.THROW,
transform(statementNode.getExpression()));
}
@Override
Node processTryStatement(TryStatement statementNode) {
Node node = new Node(Token.TRY, transform(statementNode.getTryBlock()));
Node block = new Node(Token.BLOCK);
boolean lineSet = false;
for (CatchClause cc : statementNode.getCatchClauses()) {
// Mark the enclosing block at the same line as the first catch
// clause.
if (lineSet == false) {
block.setLineno(cc.getLineno());
lineSet = true;
}
block.addChildToBack(transform(cc));
}
node.addChildToBack(block);
AstNode finallyBlock = statementNode.getFinallyBlock();
if (finallyBlock != null) {
node.addChildToBack(transform(finallyBlock));
}
// If we didn't set the line on the catch clause, then
// we've got an empty catch clause. Set its line to be the same
// as the finally block (to match Old Rhino's behavior.)
if ((lineSet == false) && (finallyBlock != null)) {
block.setLineno(finallyBlock.getLineno());
}
return node;
}
@Override
Node processUnaryExpression(UnaryExpression exprNode) {
Node node = new Node(transformTokenType(exprNode.getType()),
transform(exprNode.getOperand()));
if (exprNode.isPostfix()) {
node.putBooleanProp(Node.INCRDECR_PROP, true);
}
return node;
}
@Override
Node processVariableDeclaration(VariableDeclaration declarationNode) {
Node node = new Node(Token.VAR);
for (VariableInitializer child : declarationNode.getVariables()) {
node.addChildToBack(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>transform(child));
}
return node;
}
@Override
Node processVariableInitializer(VariableInitializer initializerNode) {
Node node = transform(initializerNode.getTarget());
if (initializerNode.getInitializer() != null) {
node.addChildToBack(transform(initializerNode.getInitializer()));
node.setLineno(node.getLineno());
}
return node;
}
@Override
Node processWhileLoop(WhileLoop loopNode) {
return new Node(
Token.WHILE,
transform(loopNode.getCondition()),
transform(loopNode.getBody()));
}
@Override
Node processWithStatement(WithStatement statementNode) {
return new Node(
Token.WITH,
transform(statementNode.getExpression()),
transform(statementNode.getStatement()));
}
@Override
Node processIllegalToken(AstNode node) {
errorReporter.error(
"Unsupported syntax: " +
com.google.javascript.jscomp.mozilla.rhino.Token.typeToName(
node.getType()),
sourceName,
node.getLineno(), "", 0);
return new Node(Token.EMPTY);
}
void reportDestructuringAssign(AstNode node) {
errorReporter.error(
"destructuring assignment forbidden",
sourceName,
node.getLineno(), "", 0);
}
}
private static int transformTokenType(int token) {
switch (token) {
case com.google.javascript.jscomp.mozilla.rhino.Token.ERROR:
return Token.ERROR;
case com.google.javascript.jscomp.mozilla.rhino.Token.EOF:
return Token.EOF;
case com.google.javascript.jscomp.mozilla.rhino.Token.EOL:
return Token.EOL;
case com.google.javascript.jscomp.mozilla.rhino.Token.ENTERWITH:
return Token.ENTERWITH;
case com.google.javascript.jscomp.mozilla.rhino.Token.LEAVEWITH:
return Token.LEAVEWITH;
case com.google.javascript.jscomp.mozilla.rhino.Token.RETURN:
return Token.RETURN;
case com.google.javascript.jscomp.mozilla.rhino.Token.GOTO:
return Token.GOTO;
case com.google
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>ino.Token.ASSIGN_DIV:
return Token.ASSIGN_DIV;
case com.google.javascript.jscomp.mozilla.rhino.Token.ASSIGN_MOD:
return Token.ASSIGN_MOD;
case com.google.javascript.jscomp.mozilla.rhino.Token.HOOK:
return Token.HOOK;
case com.google.javascript.jscomp.mozilla.rhino.Token.COLON:
return Token.COLON;
case com.google.javascript.jscomp.mozilla.rhino.Token.OR:
return Token.OR;
case com.google.javascript.jscomp.mozilla.rhino.Token.AND:
return Token.AND;
case com.google.javascript.jscomp.mozilla.rhino.Token.INC:
return Token.INC;
case com.google.javascript.jscomp.mozilla.rhino.Token.DEC:
return Token.DEC;
case com.google.javascript.jscomp.mozilla.rhino.Token.DOT:
return Token.DOT;
case com.google.javascript.jscomp.mozilla.rhino.Token.FUNCTION:
return Token.FUNCTION;
case com.google.javascript.jscomp.mozilla.rhino.Token.EXPORT:
return Token.EXPORT;
case com.google.javascript.jscomp.mozilla.rhino.Token.IMPORT:
return Token.IMPORT;
case com.google.javascript.jscomp.mozilla.rhino.Token.IF:
return Token.IF;
case com.google.javascript.jscomp.mozilla.rhino.Token.ELSE:
return Token.ELSE;
case com.google.javascript.jscomp.mozilla.rhino.Token.SWITCH:
return Token.SWITCH;
case com.google.javascript.jscomp.mozilla.rhino.Token.CASE:
return Token.CASE;
case com.google.javascript.jscomp.mozilla.rhino.Token.DEFAULT:
return Token.DEFAULT;
case com.google.javascript.jscomp.mozilla.rhino.Token.WHILE:
return Token.WHILE;
case com.google.javascript.jscomp.mozilla.rhino.Token.DO:
return Token.DO;
case com.google.javascript.jscomp.mozilla.rhino.Token
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.FOR:
return Token.FOR;
case com.google.javascript.jscomp.mozilla.rhino.Token.BREAK:
return Token.BREAK;
case com.google.javascript.jscomp.mozilla.rhino.Token.CONTINUE:
return Token.CONTINUE;
case com.google.javascript.jscomp.mozilla.rhino.Token.VAR:
return Token.VAR;
case com.google.javascript.jscomp.mozilla.rhino.Token.WITH:
return Token.WITH;
case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH:
return Token.CATCH;
case com.google.javascript.jscomp.mozilla.rhino.Token.FINALLY:
return Token.FINALLY;
case com.google.javascript.jscomp.mozilla.rhino.Token.VOID:
return Token.VOID;
case com.google.javascript.jscomp.mozilla.rhino.Token.RESERVED:
return Token.RESERVED;
case com.google.javascript.jscomp.mozilla.rhino.Token.EMPTY:
return Token.EMPTY;
case com.google.javascript.jscomp.mozilla.rhino.Token.BLOCK:
return Token.BLOCK;
case com.google.javascript.jscomp.mozilla.rhino.Token.LABEL:
return Token.LABEL;
case com.google.javascript.jscomp.mozilla.rhino.Token.TARGET:
return Token.TARGET;
case com.google.javascript.jscomp.mozilla.rhino.Token.LOOP:
return Token.LOOP;
case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_VOID:
return Token.EXPR_VOID;
case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_RESULT:
return Token.EXPR_RESULT;
case com.google.javascript.jscomp.mozilla.rhino.Token.JSR:
return Token.JSR;
case com.google.javascript.jscomp.mozilla.rhino.Token.SCRIPT:
return Token.SCRIPT;
case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOFNAME:
return Token.TYPEOFNAME;
case com.google.javascript.jscomp.mozilla.rhino.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> source = getSource();
String sourceExcerpt = source == null ? null :
excerpt.get(
source, error.sourceName, error.lineNumber, excerptFormatter);
// formatting the message
StringBuilder b = new StringBuilder();
if (error.sourceName != null) {
b.append(error.sourceName);
if (error.lineNumber > 0) {
b.append(':');
b.append(error.lineNumber);
}
b.append(": ");
}
b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR));
b.append(" - ");
b.append(error.description);
b.append('\n');
if (sourceExcerpt != null) {
b.append(sourceExcerpt);
b.append('\n');
int charno = error.getCharno();
// padding equal to the excerpt and arrow at the end
if (excerpt.equals(LINE)
&& 0 <= charno && charno < sourceExcerpt.length()) {
for (int i = 0; i < charno; i++) {
char c = sourceExcerpt.charAt(i);
if (Character.isWhitespace(c)) {
b.append(c);
} else {
b.append(' ');
}
}
b.append("^\n");
}
}
return b.toString();
}
/**
* Formats a region by appending line numbers in front, e.g.
* <pre> 9| if (foo) {
* 10| alert('bar');
* 11| }</pre>
* and return line excerpt without any modification.
*/
static class LineNumberingFormatter implements ExcerptFormatter {
public String formatLine(String line, int lineNumber) {
return line;
}
public String formatRegion(Region region) {
if (region == null) {
return null;
}
String code = region.getSourceExcerpt();
if (code.length() == 0) {
return null;
}
// max length of the number display
int numberLength = Integer.toString(region.getEndingLineNumber())
.length();
// formatting
StringBuilder builder = new StringBuilder(code.length() * 2);
int
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> CompilerInput[] getAllInputsFromModules() {
List<CompilerInput> inputs = new ArrayList<CompilerInput>();
Map<String, JSModule> inputMap = new HashMap<String, JSModule>();
for (JSModule module : modules_) {
for (CompilerInput input : module.getInputs()) {
String inputName = input.getName();
JSModule firstModule = inputMap.get(inputName);
if (firstModule == null) {
inputs.add(input);
inputMap.put(inputName, module);
} else {
report(JSError.make(DUPLICATE_INPUT_IN_MODULES,
firstModule.getName(), module.getName(), inputName));
}
}
}
if (hasErrors()) {
// There's no reason to bother parsing the code.
return new CompilerInput[0];
}
return inputs.toArray(new CompilerInput[inputs.size()]);
}
static final DiagnosticType DUPLICATE_INPUT =
DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}");
static final DiagnosticType DUPLICATE_EXTERN_INPUT =
DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT",
"Duplicate extern input: {0}");
/**
* Creates a map to make looking up an input by name fast. Also checks for
* duplicate inputs.
*/
void initInputsByNameMap() {
inputsByName_ = new HashMap<String, CompilerInput>();
for (CompilerInput input : externs_) {
String name = input.getName();
if (!inputsByName_.containsKey(name)) {
inputsByName_.put(name, input);
} else {
report(JSError.make(DUPLICATE_EXTERN_INPUT, name));
}
}
for (CompilerInput input : inputs_) {
String name = input.getName();
if (!inputsByName_.containsKey(name)) {
inputsByName_.put(name, input);
} else {
report(JSError.make(DUPLICATE_INPUT, name));
}
}
}
public Result compile(
JSSourceFile extern, JSSourceFile input, CompilerOptions options) {
return compile(extern, new JSSourceFile[] { input }, options);
}
public Result compile(
JSSourceFile extern, JSSourceFile[] input
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>, CompilerOptions options) {
return compile(new JSSourceFile[] { extern }, input, options);
}
public Result compile(
JSSourceFile extern, JSModule[] modules, CompilerOptions options) {
return compile(new JSSourceFile[] { extern }, modules, options);
}
/**
* Compiles a list of inputs.
*/
public Result compile(JSSourceFile[] externs,
JSSourceFile[] inputs,
CompilerOptions options) {
// The compile method should only be called once.
Preconditions.checkState(jsRoot == null);
try {
init(externs, inputs, options);
if (hasErrors()) {
return getResult();
}
return compile();
} finally {
Tracer t = newTracer("generateReport");
errorManager.generateReport();
stopTracer(t, "generateReport");
}
}
/**
* Compiles a list of modules.
*/
public Result compile(JSSourceFile[] externs,
JSModule[] modules,
CompilerOptions options) {
// The compile method should only be called once.
Preconditions.checkState(jsRoot == null);
try {
init(externs, modules, options);
if (hasErrors()) {
return getResult();
}
return compile();
} finally {
Tracer t = newTracer("generateReport");
errorManager.generateReport();
stopTracer(t, "generateReport");
}
}
private Result compile() {
return runInCompilerThread(new Callable<Result>() {
public Result call() throws Exception {
compileInternal();
return getResult();
}
});
}
/**
* Disable threads. This is for clients that run on AppEngine and
* don't have threads.
*/
public void disableThreads() {
useThreads = false;
}
@SuppressWarnings("unchecked")
private <T> T runInCompilerThread(final Callable<T> callable) {
// Under JRE 1.6, the jscompiler overflows the stack when running on some
// large or complex js code. Here we start a new thread with a larger
// stack in order to let the compiler do its thing, without having to
// increase the stack size for *every* thread (which is what -Xss does).
// Might want to
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
}
SourceAst ast = new SyntheticAst(name);
CompilerInput input = new CompilerInput(ast, name, true);
inputsByName_.put(name, input);
externsRoot.addChildToFront(ast.getAstRoot(this));
return input;
}
/** Add a source input dynamically. Intended for incremental compilation. */
void addIncrementalSourceAst(JsAst ast) {
String sourceName = ast.getSourceFile().getName();
Preconditions.checkState(
getInput(sourceName) == null,
"Duplicate input of name " + sourceName);
inputsByName_.put(sourceName, new CompilerInput(ast));
}
@Override
JSModuleGraph getModuleGraph() {
return moduleGraph_;
}
@Override
public JSTypeRegistry getTypeRegistry() {
if (typeRegistry == null) {
typeRegistry = new JSTypeRegistry(oldErrorReporter);
}
return typeRegistry;
}
@Override
ScopeCreator getScopeCreator() {
return getPassConfig().getScopeCreator();
}
@Override
public Scope getTopScope() {
return getPassConfig().getTopScope();
}
@Override
public ReverseAbstractInterpreter getReverseAbstractInterpreter() {
if (abstractInterpreter == null) {
ChainableReverseAbstractInterpreter interpreter =
new SemanticReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry());
if (options_.closurePass) {
interpreter = new ClosureReverseAbstractInterpreter(
getCodingConvention(), getTypeRegistry())
.append(interpreter).getFirst();
}
abstractInterpreter = interpreter;
}
return abstractInterpreter;
}
@Override
TypeValidator getTypeValidator() {
return typeValidator;
}
//------------------------------------------------------------------------
// Parsing
//------------------------------------------------------------------------
/**
* Parses the externs and main inputs.
*
* @return A synthetic root node whose two children are the externs root
* and the main root
*/
Node parseInputs() {
boolean devMode = options_.devMode != DevMode.OFF;
// If old roots exist (we are parsing a second time), detach each of the
// individual file parse trees.
if (externsRoot != null) {
externsRoot.detachChildren();
}
if (jsRoot != null) {
jsRoot.detachChildren();
}
// Parse main js sources.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> jsRoot = new Node(Token.BLOCK);
jsRoot.setIsSyntheticBlock(true);
if (options_.tracer.isOn()) {
tracker = new PerformanceTracker(jsRoot,
options_.tracer == TracerMode.ALL);
addChangeHandler(tracker.getCodeChangeHandler());
}
Tracer tracer = newTracer("parseInputs");
try {
// Parse externs sources.
externsRoot = new Node(Token.BLOCK);
externsRoot.setIsSyntheticBlock(true);
for (CompilerInput input : externs_) {
Node n = input.getAstRoot(this);
if (hasErrors()) {
return null;
}
externsRoot.addChildToBack(n);
}
for (CompilerInput input : inputs_) {
Node n = input.getAstRoot(this);
if (hasErrors()) {
return null;
}
// Inputs can have a null AST during initial parse.
if (n == null) {
continue;
}
if (devMode) {
runSanityCheck();
if (hasErrors()) {
return null;
}
}
if (options_.sourceMapOutputPath != null ||
options_.nameReferenceReportPath != null) {
// Annotate the nodes in the tree with information from the
// input file. This information is used to construct the SourceMap.
SourceInformationAnnotator sia =
new SourceInformationAnnotator(input.getName());
NodeTraversal.traverse(this, n, sia);
}
jsRoot.addChildToBack(n);
}
externAndJsRoot = new Node(Token.BLOCK, externsRoot, jsRoot);
externAndJsRoot.setIsSyntheticBlock(true);
return externAndJsRoot;
} finally {
stopTracer(tracer, "parseInputs");
}
}
public Node parse(JSSourceFile file) {
initCompilerOptionsIfTesting();
addToDebugLog("Parsing: " + file.getName());
return new JsAst(file).getAstRoot(this);
}
@Override
Node parseSyntheticCode(String js) {
CompilerInput input = new CompilerInput(
JSSourceFile.fromCode(" [synthetic] ", js));
inputsByName_.put(input.getName(), input);
return input.getAstRoot
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(this);
}
void initCompilerOptionsIfTesting() {
if (options_ == null) {
// initialization for tests that don't initialize the compiler
// by the normal mechanisms.
initOptions(new CompilerOptions());
}
}
@Override
Node parseSyntheticCode(String fileName, String js) {
initCompilerOptionsIfTesting();
return parse(JSSourceFile.fromCode(fileName, js));
}
Node parseTestCode(String js) {
initCompilerOptionsIfTesting();
CompilerInput input = new CompilerInput(
JSSourceFile.fromCode(" [testcode] ", js));
if (inputsByName_ == null) {
inputsByName_ = Maps.newHashMap();
}
inputsByName_.put(input.getName(), input);
return input.getAstRoot(this);
}
@Override
ErrorReporter getDefaultErrorReporter() {
return defaultErrorReporter;
}
//------------------------------------------------------------------------
// Convert back to source code
//------------------------------------------------------------------------
/**
* Converts the main parse tree back to js code.
*/
public String toSource() {
return runInCompilerThread(new Callable<String>() {
public String call() throws Exception {
Tracer tracer = newTracer("toSource");
try {
CodeBuilder cb = new CodeBuilder();
if (jsRoot != null) {
int i = 0;
for (Node scriptNode = jsRoot.getFirstChild();
scriptNode != null;
scriptNode = scriptNode.getNext()) {
toSource(cb, i++, scriptNode);
}
}
return cb.toString();
} finally {
stopTracer(tracer, "toSource");
}
}
});
}
/**
* Converts the parse tree for each input back to js code.
*/
public String[] toSourceArray() {
return runInCompilerThread(new Callable<String[]>() {
public String[] call() throws Exception {
Tracer tracer = newTracer("toSourceArray");
try {
int numInputs = inputs_.length;
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs_[i].getAstRoot(Compiler.this);
cb.reset();
toSource(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>();
}
@Override
boolean hasHaltingErrors() {
return !isIdeMode() && getErrorCount() > 0;
}
/**
* Consults the {@link ErrorManager} to see if we've encountered errors
* that should halt compilation. <p>
*
* If {@link CompilerOptions#ideMode} is {@code true}, this function
* always returns {@code false} without consulting the error manager. The
* error manager will continue to be told about new errors and warnings, but
* the compiler will complete compilation of all inputs.<p>
*/
public boolean hasErrors() {
return hasHaltingErrors();
}
/** Called from the compiler passes, adds debug info */
@Override
void addToDebugLog(String str) {
debugLog_.append(str);
debugLog_.append('\n');
logger_.fine(str);
}
private SourceFile getSourceFileByName(String sourceName) {
if (inputsByName_.containsKey(sourceName)) {
return inputsByName_.get(sourceName).getSourceFile();
}
return null;
}
public String getSourceLine(String sourceName, int lineNumber) {
if (lineNumber < 1) {
return null;
}
SourceFile input = getSourceFileByName(sourceName);
if (input != null) {
return input.getLine(lineNumber);
}
return null;
}
public Region getSourceRegion(String sourceName, int lineNumber) {
if (lineNumber < 1) {
return null;
}
SourceFile input = getSourceFileByName(sourceName);
if (input != null) {
return input.getRegion(lineNumber);
}
return null;
}
//------------------------------------------------------------------------
// Package-private helpers
//------------------------------------------------------------------------
@Override
Node getNodeForCodeInsertion(JSModule module) {
if (module == null) {
if (inputs_.length == 0) {
throw new IllegalStateException("No inputs");
}
return inputs_[0].getAstRoot(this);
}
List<CompilerInput> inputs = module.getInputs();
if (inputs.size() > 0) {
return inputs.get(0).getAstRoot(this);
}
for (JSModule m : getModuleGraph().getTransitiveDepsDeepestFirst
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> null;
if (parent == null) {
scope = new Scope(n, compiler);
} else {
scope = new Scope(parent, n);
}
scanRoot(n, parent);
sourceName = null;
Scope returnedScope = scope;
scope = null;
return returnedScope;
}
private void scanRoot(Node n, Scope parent) {
if (n.getType() == Token.FUNCTION) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
final Node fnNameNode = n.getFirstChild();
final Node args = fnNameNode.getNext();
final Node body = args.getNext();
// Bleed the function name into the scope, if it hasn't
// been declared in the outer scope.
String fnName = fnNameNode.getString();
if (!fnName.isEmpty() && NodeUtil.isFunctionAnonymous(n)) {
declareVar(fnName, fnNameNode, n, null, null, n);
}
// Args: Declare function variables
Preconditions.checkState(args.getType() == Token.LP);
for (Node a = args.getFirstChild(); a != null;
a = a.getNext()) {
Preconditions.checkState(a.getType() == Token.NAME);
declareVar(a.getString(), a, args, n, null, n);
}
// Body
scanVars(body, n);
} else {
// It's the global block
Preconditions.checkState(scope.getParent() == null);
scanVars(n, null);
}
}
/**
* Scans and gather variables declarations under a Node
*/
private void scanVars(Node n, Node parent) {
switch (n.getType()) {
case Token.VAR:
// Declare all variables. e.g. var x = 1, y, z;
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
Preconditions.checkState(child.getType() == Token.NAME);
String name = child.getString();
declareVar(name, child, n, parent, null, n);
child = next;
}
return;
case Token.FUNCTION:
if (NodeUtil.isFunctionAnonymous(n)) {
return;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(fnName, n.getFirstChild(), n, parent, null, n);
return; // should not examine function's children
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getFirstChild().getType() == Token.NAME);
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext().getNext();
declareVar(var.getString(), var, n, parent, null, n);
scanVars(block, n);
return; // only one child to scan
case Token.SCRIPT:
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child, n);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name,
Node n, Node parent, Node gramps, Node nodeWithLineNumber);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
public void onRedeclaration(
Scope s, String name,
Node n, Node parent, Node gramps, Node nodeWithLineNumber) {
// Don't allow multiple variables to be declared at the top level scope
if (scope.isGlobal()) {
Scope.Var origVar = scope.getVar(name);
Node origParent = origVar.getParentNode();
if (origParent.getType() == Token.CATCH &&
parent.getType() == Token.CATCH) {
// Okay, both are 'catch(x)' variables.
return
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>;
}
boolean allowDupe = false;
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
if (!allowDupe) {
compiler.report(
JSError.make(sourceName, nodeWithLineNumber,
VAR_MULTIPLY_DECLARED_ERROR,
name,
(origVar.input != null
? origVar.input.getName()
: "??")));
}
}
}
}
/**
* Declares a variable.
*
* @param name The variable name
* @param n The node corresponding to the variable name (usually a NAME node)
* @param parent The parent node of {@code n}
* @param gramps The parent node of {@code parent}
* @param declaredType The variable's type, according to JSDoc
* @param nodeWithLineNumber The node to use to access the line number of
* the variable declaration, if needed
*/
private void declareVar(String name, Node n, Node parent,
Node gramps, JSType declaredType,
Node nodeWithLineNumber) {
if (scope.isDeclared(name, false)
|| (scope.isLocal() && name.equals(ARGUMENTS))) {
redeclarationHandler.onRedeclaration(
scope, name, n, parent, gramps, nodeWithLineNumber);
} else {
scope.declare(name, n, declaredType, compiler.getInput(sourceName));
}
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.ObjectType;
import java.nio.charset.Charset;
/**
* A code generator that outputs type annotations for functions and
* constructors.
*
*/
class TypedCodeGenerator extends CodeGenerator {
TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) {
super(consumer, outputCharset, true);
}
@Override
void add(Node n, Context context) {
Node parent = n.getParent();
if (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT) {
if (n.getType() == Token.FUNCTION) {
add(getFunctionAnnotation(n));
} else if (n.getType() == Token.EXPR_RESULT
&& n.getFirstChild().getType() == Token.ASSIGN) {
Node rhs = n.getFirstChild().getFirstChild();
add(getTypeAnnotation(rhs));
} else if (n.getType() == Token.VAR
&& n.getFirstChild().getFirstChild() != null
&& n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) {
add(getFunctionAnnotation(n.getFirstChild().getFirstChild()));
}
}
super.add(n, context);
}
private String getTypeAnnotation(Node node) {
JSType type = node.getJSType();
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
if (type instanceof FunctionType) {
return getFunctionAnnotation(node);
} else if (type != null && !type.isUnknownType()
&& !type.isEmptyType() && !type.isVoidType()) {
return "/** @type {" + node.getJSType() + "} */\n";
} else {
return "";
}
}
/**
* @param node A node for a function for which to generate a type annotation
*/
private String getFunctionAnnotation(Node node) {
StringBuilder sb = new StringBuilder("/**\n");
if (node.getJSType().isUnknownType()) {
return "";
}
FunctionType funType = (FunctionType) node.getJSType();
// We need to use the child nodes of the function as the nodes for the
// parameters of the function type do not have the real parameter names.
// FUNCTION
// NAME
// LP
// NAME param1
// NAME param2
Node fnNode = funType.getSource();
if (fnNode != null) {
Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild();
// Param types
for (Node n : funType.getParameters()) {
// Bail out if the paramNode is not there.
if (paramNode == null) {
break;
}
sb.append(" * @param {" + n.getJSType() + "} ");
sb.append(paramNode.getString());
sb.append("\n");
paramNode = paramNode.getNext();
}
}
// Return type
JSType retType = funType.getReturnType();
if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) {
sb.append(" * @return {" + retType + "}\n");
}
// Constructor/interface
if (funType.isConstructor() || funType.isInterface()) {
ObjectType superInstance =
funType.getSuperClassConstructor().getInstanceType();
if (!superInstance.toString().equals("Object")) {
sb.append(" * @extends {" + superInstance + "}\n");
}
for (ObjectType interfaze : funType.getImplementedInterfaces()) {
sb.append(" * @implements {" + interfaze + "}\n");
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> {
this.rootRenamer = new ContextualRenamer();
}
MakeDeclaredNamesUnique(Renamer renamer) {
this.rootRenamer = renamer;
}
static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) {
return new ContextualRenameInverter(compiler);
}
@Override
public void enterScope(NodeTraversal t) {
Node declarationRoot = t.getScopeRoot();
Renamer renamer;
if (nameStack.isEmpty()) {
// If the contextual renamer is being used the starting context can not
// be a function.
Preconditions.checkState(
declarationRoot.getType() != Token.FUNCTION ||
!(rootRenamer instanceof ContextualRenamer));
Preconditions.checkState(t.inGlobalScope());
renamer = rootRenamer;
} else {
renamer = nameStack.peek().forChildScope();
}
if (declarationRoot.getType() == Token.FUNCTION) {
// Add the function parameters
Node fnParams = declarationRoot.getFirstChild().getNext();
for (Node c = fnParams.getFirstChild(); c != null; c = c.getNext()) {
String name = c.getString();
renamer.addDeclaredName(name);
}
// Add the function body declarations
Node functionBody = declarationRoot.getLastChild();
findDeclaredNames(functionBody, null, renamer);
} else {
// Add the block declarations
findDeclaredNames(declarationRoot, null, renamer);
}
nameStack.push(renamer);
}
@Override
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
nameStack.pop();
}
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.FUNCTION:
{
// Add recursive function name, if needed.
// NOTE: "enterScope" is called after we need to pick up this name.
Renamer renamer = nameStack.peek().forChildScope();
// If needed, add the function recursive name.
String name = n.getFirstChild().getString();
if (name != null && !name.isEmpty() && parent != null
&& !NodeUtil.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>isFunctionDeclaration(n)) {
renamer.addDeclaredName(name);
}
nameStack.push(renamer);
}
break;
case Token.CATCH:
{
Renamer renamer = nameStack.peek().forChildScope();
String name = n.getFirstChild().getString();
renamer.addDeclaredName(name);
nameStack.push(renamer);
}
break;
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
String newName = getReplacementName(n.getString());
if (newName != null) {
Renamer renamer = nameStack.peek();
if (renamer.stripConstIfReplaced()) {
// TODO(johnlenz): Do we need to do anything about the javadoc?
n.removeProp(Node.IS_CONSTANT_NAME);
}
n.setString(newName);
t.getCompiler().reportCodeChange();
}
break;
case Token.FUNCTION:
// Remove function recursive name (if any).
nameStack.pop();
break;
case Token.CATCH:
// Remove catch except name from the stack of names.
nameStack.pop();
break;
}
}
/**
* Walks the stack of name maps and finds the replacement name for the
* current scope.
*/
private String getReplacementName(String oldName) {
for (Renamer names : nameStack) {
String newName = names.getReplacementName(oldName);
if (newName != null) {
return newName;
}
}
return null;
}
/**
* Traverses the current scope and collects declared names. Does not
* decent into functions or add CATCH exceptions.
*/
private void findDeclaredNames(Node n, Node parent, Renamer renamer) {
// Do a shallow traversal, so don't traverse into function declarations,
// except for the name of the function itself.
if (parent == null
|| parent.getType() != Token.FUNCTION
|| n == parent.getFirstChild()) {
if (NodeUtil.isVarDeclaration(n)) {
renamer.addDeclaredName(n.getString());
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
/**
* Prepare a set for the new scope.
*/
public void enterScope(NodeTraversal t) {
if (t.inGlobalScope()) {
return;
}
referenceStack.push(referencedNames);
referencedNames = Sets.newHashSet();
}
/**
* Rename vars for the current scope, and merge any referenced
* names into the parent scope reference set.
*/
public void exitScope(NodeTraversal t) {
if (t.inGlobalScope()) {
return;
}
for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) {
Var v = it.next();
handleScopeVar(v);
}
// Merge any names that were referenced but not declared in the current
// scope.
Set<String> current = referencedNames;
referencedNames = referenceStack.pop();
// If there isn't anything left in the stack we will be going into the
// global scope: don't try to build a set of referenced names for the
// global scope.
if (!referenceStack.isEmpty()) {
referencedNames.addAll(current);
}
}
/**
* For the Var declared in the current scope determine if it is possible
* to revert the name to its orginal form without conflicting with other
* values.
*/
void handleScopeVar(Var v) {
String name = v.getName();
if (containsSeparator(name)) {
String newName = getOrginalName(name);
// Check if the new name is valid and if it would cause conflicts.
if (TokenStream.isJSIdentifier(newName) &&
!referencedNames.contains(newName) &&
!newName.equals(ARGUMENTS)) {
referencedNames.remove(name);
// Adding a reference to the new name to prevent either the parent
// scopes or the current scope renaming another var to this new name.
referencedNames.add(newName);
List<Node> references = nameMap.get(name);
Preconditions.checkState(references != null);
for (Node n : references) {
Preconditions.checkState(n.getType() == Token.NAME);
n.setString(newName);
}
compiler.reportCodeChange();
}
nameMap.remove(name);
}
}
@Override
public
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
private JSType currentClass = null;
CheckAccessControls(AbstractCompiler compiler) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
}
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
public void enterScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth++;
}
if (methodDepth == 0) {
currentClass = getClassOfMethod(n, parent);
}
methodDepth++;
}
}
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
Node n = t.getScopeRoot();
Node parent = n.getParent();
if (isDeprecatedFunction(n, parent)) {
deprecatedDepth--;
}
methodDepth--;
if (methodDepth == 0) {
currentClass = null;
}
}
}
/**
* Gets the type of the class that "owns" a method, or null if
* we know that its un-owned.
*/
private JSType getClassOfMethod(Node n, Node parent) {
if (parent.getType() == Token.ASSIGN) {
Node lValue = parent.getFirstChild();
if (lValue.isQualifiedName()) {
if (lValue.getType() == Token.GETPROP) {
// We have an assignment of the form "a.b = ...".
JSType lValueType = lValue.getJSType();
if (lValueType != null && lValueType.isConstructor()) {
// If a.b is a constructor, then everything in this function
// belongs to the "a.b" type.
return ((FunctionType) lValueType).getInstanceType();
} else {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> } else if (NodeUtil.isFunctionDeclaration(n) ||
parent.getType() == Token.NAME) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isConstructor()) {
return ((FunctionType) type).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Node parent) {
JSType type = n.getJSType();
if (type != null) {
String deprecationInfo = getTypeDeprecationInfo(type);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
JSError.make(t, n, DEPRECATED_CLASS_REASON,
type.toString(), deprecationInfo));
} else {
compiler.report(
JSError.make(t, n, DEPRECATED_CLASS, type.toString()));
}
}
}
}
/**
* Checks the given NAME node to ensure that access restrictions are obeyed.
*/
private
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> void checkNameDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking definitions or constructors.
if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR ||
parent.getType() == Token.NEW) {
return;
}
Scope.Var var = t.getScope().getVar(n.getString());
JSDocInfo docInfo = var == null ? null : var.getJSDocInfo();
if (docInfo != null && docInfo.isDeprecated() &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (docInfo.getDeprecationReason() != null) {
compiler.report(
JSError.make(t, n, DEPRECATED_NAME_REASON, n.getString(),
docInfo.getDeprecationReason()));
} else {
compiler.report(
JSError.make(t, n, DEPRECATED_NAME, n.getString()));
}
}
}
/**
* Checks the given GETPROP node to ensure that access restrictions are
* obeyed.
*/
private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking constructors.
if (parent.getType() == Token.NEW) {
return;
}
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getJSType()));
String propertyName = n.getLastChild().getString();
if (objectType != null) {
String deprecationInfo
= getPropertyDeprecationInfo(objectType, propertyName);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
JSError.make(t, n, DEPRECATED_PROP_REASON, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true),
deprecationInfo));
} else {
compiler.report(
JSError.make(t, n, DEPRECATED_PROP, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true)));
}
}
}
}
/**
* Determines whether the given name is visible in the current context.
* @param t The current traversal.
* @param name The name node.
*/
private void checkNameVisibility(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>NodeTraversal t, Node name, Node parent) {
Var var = t.getScope().getVar(name.getString());
if (var != null) {
JSDocInfo docInfo = var.getJSDocInfo();
if (docInfo != null) {
// If a name is private, make sure that we're in the same file.
Visibility visibility = docInfo.getVisibility();
if (visibility == Visibility.PRIVATE &&
!t.getInput().getName().equals(docInfo.getSourceName())) {
if (docInfo.isConstructor() &&
isValidPrivateConstructorAccess(parent)) {
return;
}
compiler.report(
JSError.make(t, name, BAD_PRIVATE_GLOBAL_ACCESS,
name.getString(), docInfo.getSourceName()));
}
}
}
}
/**
* Determines whether the given property is visible in the current context.
* @param t The current traversal.
* @param getprop The getprop node.
*/
private void checkPropertyVisibility(NodeTraversal t,
Node getprop, Node parent) {
ObjectType objectType =
ObjectType.cast(dereference(getprop.getFirstChild().getJSType()));
String propertyName = getprop.getLastChild().getString();
if (objectType != null) {
// Is this a normal property access, or are we trying to override
// an existing property?
boolean isOverride = t.inGlobalScope() &&
parent.getType() == Token.ASSIGN &&
parent.getFirstChild() == getprop;
// Find the lowest property defined on a class with visibility
// information.
if (isOverride) {
objectType = objectType.getImplicitPrototype();
}
JSDocInfo docInfo = null;
for (; objectType != null;
objectType = objectType.getImplicitPrototype()) {
docInfo = objectType.getOwnPropertyJSDocInfo(propertyName);
if (docInfo != null &&
docInfo.getVisibility() != Visibility.INHERITED) {
break;
}
}
if (objectType == null) {
// We couldn't find a visibility modifier; assume it's public.
return;
}
boolean sameInput =
t.getInput().getName().equals(docInfo.getSourceName());
Visibility visibility = docInfo.getVisibility();
JSType owner
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>getParent();
return
// Case #1
(deprecatedDepth > 0) ||
// Case #2
(getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) ||
// Case #3
(scopeRootParent != null && scopeRootParent.getType() == Token.ASSIGN &&
getTypeDeprecationInfo(
getClassOfMethod(scopeRoot, scopeRootParent)) != null);
}
/**
* Returns whether this is a function node annotated as deprecated.
*/
private static boolean isDeprecatedFunction(Node n, Node parent) {
if (n.getType() == Token.FUNCTION) {
JSType type = n.getJSType();
if (type != null) {
return getTypeDeprecationInfo(type) != null;
}
}
return false;
}
/**
* Returns the deprecation reason for the type if it is marked
* as being deprecated. Returns empty string if the type is deprecated
* but no reason was given. Returns null if the type is not deprecated.
*/
private static String getTypeDeprecationInfo(JSType type) {
if (type == null) {
return null;
}
JSDocInfo info = type.getJSDocInfo();
if (info != null && info.isDeprecated()) {
if (info.getDeprecationReason() != null) {
return info.getDeprecationReason();
}
return "";
}
ObjectType objType = ObjectType.cast(type);
if (objType != null) {
ObjectType implicitProto = objType.getImplicitPrototype();
if (implicitProto != null) {
return getTypeDeprecationInfo(implicitProto);
}
}
return null;
}
/**
* Returns the deprecation reason for the property if it is marked
* as being deprecated. Returns empty string if the property is deprecated
* but no reason was given. Returns null if the property is not deprecated.
*/
private static String getPropertyDeprecationInfo(ObjectType type,
String prop) {
JSDocInfo info = type.getOwnPropertyJSDocInfo(prop);
if (info != null && info.isDeprecated()) {
if (info.getDeprecationReason() != null) {
return info.getDeprecationReason();
}
return "";
}
ObjectType implicitProto = type.getImplicitPrototype();
if
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>> callbacks) {
Preconditions.checkArgument(callbacks.size() > 0);
Callback[] array = callbacks.toArray(new Callback[callbacks.size()]);
return new CombinedCompilerPass(compiler, array);
}
/** A compiler pass that resolves types in the global scope. */
private class GlobalTypeResolver implements CompilerPass {
private final AbstractCompiler compiler;
GlobalTypeResolver(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (topScope == null) {
typedScopeCreator =
new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = typedScopeCreator.createScope(root.getParent(), null);
} else {
compiler.getTypeRegistry().resolveTypesInScope(topScope);
}
}
}
/** Checks global name usage. */
private final PassFactory checkGlobalNames =
new PassFactory("Check names", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
// Create a global namespace for analysis by check passes.
// Note that this class does all heavy computation lazily,
// so it's OK to create it here.
namespaceForChecks = new GlobalNamespace(compiler, jsRoot);
new CheckGlobalNames(compiler, options.checkGlobalNamesLevel)
.injectNamespace(namespaceForChecks).process(externs, jsRoot);
}
};
}
};
/** Checks for properties that are not read or written */
private final PassFactory checkSuspiciousProperties =
new PassFactory("checkSuspiciousProperties", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new SuspiciousPropertiesCheck(
compiler,
options.checkUndefinedProperties,
options.checkUnusedPropertiesEarly ?
CheckLevel.WARNING : CheckLevel.OFF);
}
};
/** Checks that the code is ES5 or Caja compliant. */
private final PassFactory checkStrictMode =
new PassFactory("checkStrictMode", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new StrictModeCheck(compiler,
!options.checkSymbols, // don't check
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> FunctionParamBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Add parameters of the given type to the end of the param list.
* @return False if this is called after optional params are added.
*/
public boolean addRequiredParams(JSType ...types) {
if (hasOptionalOrVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(type);
}
return true;
}
/**
* Add optional parameters of the given type to the end of the param list.
* @param types Types for each optional parameter. The builder will make them
* undefineable.
* @return False if this is called after var args are added.
*/
public boolean addOptionalParams(JSType ...types) {
if (hasVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(registry.createOptionalType(type)).setOptionalArg(true);
}
return true;
}
/**
* Add variable arguments to the end of the parameter list.
* @return False if this is called after var args are added.
*/
public boolean addVarArgs(JSType type) {
if (hasVarArgs()) {
return false;
}
// There are two types of variable argument functions:
// 1) Programmer-defined var args
// 2) Native bottom types that can accept any argument.
// For the first one, "undefined" is a valid value for all arguments.
// For the second, we do not want to cast it up to undefined.
if (!type.isEmptyType()) {
type = registry.createOptionalType(type);
}
newParameter(type).setVarArgs(true);
return true;
}
/**
* Copies the parameter specification from the given node.
*/
public void newParameterFromNode(Node n) {
Node newParam = newParameter(n.getJSType());
newParam.setVarArgs(n.isVarArgs());
newParam.setOptionalArg(n.isOptionalArg());
}
// Add a parameter to the list with the given type.
private Node newParameter(JSType type) {
Node paramNode = Node.newString(Token.NAME, "");
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>";
if (d == Double.NEGATIVE_INFINITY)
return "-Infinity";
if (d == 0.0)
return "0";
if ((base < 2) || (base > 36)) {
throw Context.reportRuntimeError1(
"msg.bad.radix", Integer.toString(base));
}
if (base != 10) {
return DToA.JS_dtobasestr(base, d);
} else {
StringBuffer result = new StringBuffer();
DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
return result.toString();
}
}
/**
* If str is a decimal presentation of Uint32 value, return it as long.
* Othewise return -1L;
*/
public static long testUint32String(String str)
{
// The length of the decimal string representation of
// UINT32_MAX_VALUE, 4294967296
final int MAX_VALUE_LENGTH = 10;
int len = str.length();
if (1 <= len && len <= MAX_VALUE_LENGTH) {
int c = str.charAt(0);
c -= '0';
if (c == 0) {
// Note that 00,01 etc. are not valid Uint32 presentations
return (len == 1) ? 0L : -1L;
}
if (1 <= c && c <= 9) {
long v = c;
for (int i = 1; i != len; ++i) {
c = str.charAt(i) - '0';
if (!(0 <= c && c <= 9)) {
return -1;
}
v = 10 * v + c;
}
// Check for overflow
if ((v >>> 32) == 0) {
return v;
}
}
}
return -1;
}
static boolean isSpecialProperty(String s)
{
return s.equals("__proto__") || s.equals("__parent__");
}
// ------------------
// Statements
// ------------------
public static String getMessage0(String messageId)
{
return getMessage(messageId, null);
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import java.util.List;
import java.util.Map;
/**
* CodingConvention defines a set of hooks to customize the behavior of the
* Compiler for a specific team/company.
*
*
*
*/
public class DefaultCodingConvention implements CodingConvention {
@Override
public boolean isConstant(String variableName) {
return false;
}
@Override
public boolean isValidEnumKey(String key) {
return key != null && key.length() > 0;
}
@Override
public boolean isOptionalParameter(Node parameter) {
// be as lax as possible, but this must be mutually exclusive from
// var_args parameters.
return !isVarArgsParameter(parameter);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
// be as lax as possible
return parameter.getParent().getLastChild() == parameter;
}
@Override
public boolean isExported(String name, boolean local) {
return local && name.startsWith("$super");
}
@Override
public boolean isExported(String name) {
return isExported(name, false) || isExported(name, true);
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public SubclassRelationship getClassesDefinedBy
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Name The source file name
* @param lineno Line number with source file, or -1 if unknown
* @param charno Column number within line, or -1 for whole line.
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, int lineno, int charno,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, lineno, charno, type, null, arguments);
}
/**
* Creates a JSError at a given source location
*
* @param sourceName The source file name
* @param lineno Line number with source file, or -1 if unknown
* @param charno Column number within line, or -1 for whole line.
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, int lineno, int charno,
CheckLevel level, DiagnosticType type, String... arguments) {
return new JSError(sourceName, lineno, charno, type, level, arguments);
}
/**
* Creates a JSError from a file and Node position.
*
* @param sourceName The source file name
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, Node n,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, n, type, arguments);
}
/**
* Creates a JSError from a file and Node position.
*
* @param sourceName The source file name
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, Node n, CheckLevel level,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, n.getLineno(), n.getCharno(), type, level,
arguments);
}
/**
* Creates a JSError during NodeTraversal.
*
* @param t Determines source file name containing current script
* @param
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(NodeTraversal t, Node n,
CheckLevel level, DiagnosticType type, String... arguments) {
return new JSError(t.getSourceName(), n.getLineno(), n.getCharno(), type,
level, arguments);
}
/**
* Creates a JSError during NodeTraversal.
*
* @param t Determines source file name containing current script
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(NodeTraversal t, Node n,
DiagnosticType type, String... arguments) {
return new JSError(t.getSourceName(), n, type, arguments);
}
//
// JSError constructors
//
/**
* Creates a JSError at a CheckLevel for a source file location. Package
* private to avoid any entanglement with code outside of the compiler.
*
* This is a preferred internal constructor.
*/
private JSError(String sourceName, int lineno, int charno,
DiagnosticType type, CheckLevel level, String... arguments) {
this.type = type;
this.description = type.format.format(arguments);
this.lineNumber = lineno;
this.charno = charno;
this.sourceName = sourceName;
this.level = level == null ? type.level : level;
}
/**
* Creates a JSError for a source file location. Package private to avoid
* any entanglement with code outside of the compiler.
*
* This is a preferred internal constructor.
*/
private JSError(String sourceName, Node node,
DiagnosticType type, String... arguments) {
this(sourceName,
(node != null) ? node.getLineno() : -1,
(node != null) ? node.getCharno() : -1,
type, null, arguments);
}
public DiagnosticType getType() {
return type;
}
/**
* Format a message at the given level.
*
* @return the formatted message or {@code null
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>}
*/
public String format(CheckLevel level, MessageFormatter formatter) {
switch (level) {
case ERROR:
return formatter.formatError(this);
case WARNING:
return formatter.formatWarning(this);
default:
return null;
}
}
@Override
public String toString() {
// TODO(user): remove custom toString.
return type.key + ". " + description + " at " +
(sourceName != null && sourceName.length() > 0 ?
sourceName : "(unknown source)") + " line " +
(lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)");
}
/**
* Get the character number.
*/
public int getCharno() {
return charno;
}
@Override
public boolean equals(Object o) {
// Generated by Intellij IDEA
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
JSError jsError = (JSError) o;
if (charno != jsError.charno) {
return false;
}
if (lineNumber != jsError.lineNumber) {
return false;
}
if (!description.equals(jsError.description)) {
return false;
}
if (level != jsError.level) {
return false;
}
if (sourceName != null ? !sourceName.equals(jsError.sourceName)
: jsError.sourceName != null) {
return false;
}
if (!type.equals(jsError.type)) {
return false;
}
return true;
}
@Override
public int hashCode() {
// Generated by Intellij IDEA
int result = type.hashCode();
result = 31 * result + description.hashCode();
result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0);
result = 31 * result + lineNumber;
result = 31 * result + level.hashCode();
result = 31 * result + charno;
return result;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>) {
return processBlock((Block) node);
} else if (node instanceof Scope) {
return processScope((Scope) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.BREAK:
return processBreakStatement((BreakStatement) node);
case Token.CALL:
return processFunctionCall((FunctionCall) node);
case Token.CASE:
case Token.DEFAULT:
return processSwitchCase((SwitchCase) node);
case Token.CATCH:
case Token.FINALLY:
return processCatchClause((CatchClause) node);
case Token.COLON:
return processObjectProperty((ObjectProperty) node);
case Token.CONTINUE:
return processContinueStatement((ContinueStatement) node);
case Token.DO:
return processDoLoop((DoLoop) node);
case Token.EMPTY:
return processEmptyExpression((EmptyExpression) node);
case Token.EXPR_RESULT:
case Token.EXPR_VOID:
if (node instanceof ExpressionStatement) {
return processExpressionStatement((ExpressionStatement) node);
} else if (node instanceof LabeledStatement) {
return processLabeledStatement((LabeledStatement) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.DEBUGGER:
case Token.FALSE:
case Token.NULL:
case Token.THIS:
case Token.TRUE:
return processKeywordLiteral((KeywordLiteral) node);
case Token.FOR:
if (node instanceof ForInLoop) {
return processForInLoop((ForInLoop) node);
} else if (node instanceof ForLoop) {
return processForLoop((ForLoop) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.FUNCTION:
return processFunctionNode((FunctionNode) node);
case Token.GETELEM:
return processElementGet((ElementGet) node);
case Token.GETPROP:
return processPropertyGet
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> know to be safe
switch (n.getType()) {
// other side-effect free statements and expressions
case Token.AND:
case Token.BLOCK:
case Token.EXPR_RESULT:
case Token.HOOK:
case Token.IF:
case Token.IN:
case Token.LP:
case Token.NUMBER:
case Token.OR:
case Token.THIS:
case Token.TRUE:
case Token.FALSE:
case Token.NULL:
case Token.STRING:
case Token.SWITCH:
case Token.TRY:
case Token.EMPTY:
break;
// Throws are by definition side effects
case Token.THROW:
return true;
case Token.OBJECTLIT:
case Token.ARRAYLIT:
case Token.REGEXP:
if (checkForNewObjects) {
return true;
}
break;
case Token.VAR: // empty var statement (no declaration)
case Token.NAME: // variable by itself
if (n.getFirstChild() != null)
return true;
break;
case Token.FUNCTION:
// Anonymous functions don't have side-effects, but named ones
// change the namespace. Therefore, we check if the function has
// a name. Either way, we don't need to check the children, since
// they aren't executed at declaration time.
//
return !isFunctionAnonymous(n);
case Token.NEW:
{
if (checkForNewObjects) {
return true;
}
// calls to constructors that have no side effects have the
// no side effect property set.
if (n.isNoSideEffectsCall()) {
break;
}
// certain constructors are certified side effect free
Node constructor = n.getFirstChild();
if (Token.NAME == constructor.getType()) {
String className = constructor.getString();
if (CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(className)) {
// loop below will see if the constructor parameters have
// side-effects
break;
}
} else {
// the constructor could also be an expression like
// new (useArray ? Object : Array)();
}
}
return true;
case Token.CALL:
// calls to functions that have no side effects have the no
// side effect property set.
if (n.isNoSide
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>EffectsCall()) {
// loop below will see if the function parameters have
// side-effects
break;
}
return true;
default:
if (isSimpleOperatorType(n.getType()))
break;
if (isAssignmentOp(n)) {
// Assignments will have side effects if
// a) The RHS has side effects, or
// b) The LHS has side effects, or
// c) A name on the LHS will exist beyond the life of this statement.
if (checkForStateChangeHelper(
n.getFirstChild(), checkForNewObjects) ||
checkForStateChangeHelper(
n.getLastChild(), checkForNewObjects)) {
return true;
}
Node current = n.getFirstChild();
for (;
current.getType() == Token.GETPROP ||
current.getType() == Token.GETELEM;
current = current.getFirstChild()) { }
return !(isLiteralValue(current) ||
current.getType() == Token.FUNCTION);
}
return true;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (checkForStateChangeHelper(c, checkForNewObjects)) {
return true;
}
}
return false;
}
/**
* Do calls to this constructor have side effects?
*
* @param callNode - construtor call node
*/
static boolean constructorCallHasSideEffects(Node callNode) {
Preconditions.checkArgument(
callNode.getType() == Token.NEW,
"Expected NEW node, got " + Token.name(callNode.getType()));
if (callNode.isNoSideEffectsCall()) {
return false;
}
Node nameNode = callNode.getFirstChild();
if (nameNode.getType() == Token.NAME &&
CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) {
return false;
}
return true;
}
/**
* Returns true if calls to this function have side effects.
*
* @param callNode - function call node
*/
static boolean functionCallHasSideEffects(Node callNode) {
Preconditions.checkArgument(
callNode.getType() == Token.CALL,
"Expected CALL node, got " + Token.name(callNode.getType()));
if (
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>BITAND: return 7;
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE: return 8;
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
case Token.INSTANCEOF:
case Token.IN: return 9;
case Token.LSH:
case Token.RSH:
case Token.URSH: return 10;
case Token.SUB:
case Token.ADD: return 11;
case Token.MUL:
case Token.MOD:
case Token.DIV: return 12;
case Token.INC:
case Token.DEC:
case Token.NEW:
case Token.DELPROP:
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: return 13;
case Token.ARRAYLIT:
case Token.CALL:
case Token.EMPTY:
case Token.FALSE:
case Token.FUNCTION:
case Token.GETELEM:
case Token.GETPROP:
case Token.GET_REF:
case Token.IF:
case Token.LP:
case Token.NAME:
case Token.NULL:
case Token.NUMBER:
case Token.OBJECTLIT:
case Token.REGEXP:
case Token.RETURN:
case Token.STRING:
case Token.THIS:
case Token.TRUE:
return 15;
default: throw new Error("Unknown precedence for " +
Node.tokenToName(type) +
" (type " + type + ")");
}
}
/**
* Returns true if the operator is associative.
* e.g. (a * b) * c = a * (b * c)
* Note: "+" is not associative because it is also the concatentation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
*/
static boolean isAssociative(int type) {
switch (type) {
case Token.MUL:
case Token.AND:
case Token.OR:
case Token.BITOR:
case Token
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.BITAND:
return true;
default:
return false;
}
}
static boolean isAssignmentOp(Node n) {
switch (n.getType()){
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
return true;
}
return false;
}
static int getOpFromAssignmentOp(Node n) {
switch (n.getType()){
case Token.ASSIGN_BITOR:
return Token.BITOR;
case Token.ASSIGN_BITXOR:
return Token.BITXOR;
case Token.ASSIGN_BITAND:
return Token.BITAND;
case Token.ASSIGN_LSH:
return Token.LSH;
case Token.ASSIGN_RSH:
return Token.RSH;
case Token.ASSIGN_URSH:
return Token.URSH;
case Token.ASSIGN_ADD:
return Token.ADD;
case Token.ASSIGN_SUB:
return Token.SUB;
case Token.ASSIGN_MUL:
return Token.MUL;
case Token.ASSIGN_DIV:
return Token.DIV;
case Token.ASSIGN_MOD:
return Token.MOD;
}
throw new IllegalArgumentException("Not an assiment op");
}
static boolean isExpressionNode(Node n) {
return n.getType() == Token.EXPR_RESULT;
}
/**
* Determines if the given node contains a function declaration.
*/
static boolean containsFunctionDeclaration(Node n) {
return containsType(n, Token.FUNCTION);
}
/**
* Returns true if the subtree contains references to 'this' keyword
*/
static boolean referencesThis(Node n) {
return containsType(n, Token.THIS);
}
/**
* Is this a GETPROP or GETELEM node?
*/
static boolean isGet(Node n) {
return n.getType() == Token.GETPROP
|| n.getType() == Token.GETELEM;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> /**
* Is this a GETPROP node?
*/
static boolean isGetProp(Node n) {
return n.getType() == Token.GETPROP;
}
/**
* Is this a NAME node?
*/
static boolean isName(Node n) {
return n.getType() == Token.NAME;
}
/**
* Is this a NEW node?
*/
static boolean isNew(Node n) {
return n.getType() == Token.NEW;
}
/**
* Is this a VAR node?
*/
static boolean isVar(Node n) {
return n.getType() == Token.VAR;
}
/**
* Is this node the name of a variable being declared?
*
* @param n The node
* @return True if {@code n} is NAME and {@code parent} is VAR
*/
static boolean isVarDeclaration(Node n) {
// There is no need to verify that parent != null because a NAME node
// always has a parent in a valid parse tree.
return n.getType() == Token.NAME && n.getParent().getType() == Token.VAR;
}
/**
* For an assignment or variable declaration get the assigned value.
* @return The value node representing the new value.
*/
static Node getAssignedValue(Node n) {
Preconditions.checkState(isName(n));
Node parent = n.getParent();
if (isVar(parent)) {
return n.getFirstChild();
} else if (isAssign(parent) && parent.getFirstChild() == n) {
return n.getNext();
} else {
return null;
}
}
/**
* Is this a STRING node?
*/
static boolean isString(Node n) {
return n.getType() == Token.STRING;
}
/**
* Is this node an assignment expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is ASSIGN
*/
static boolean isExprAssign(Node n) {
return n.getType() == Token.EXPR_RESULT
&& n.getFirstChild().getType() == Token.ASSIGN;
}
/**
* Is this an ASSIGN node?
*/
static boolean isAssign(Node
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> n) {
return n.getType() == Token.ASSIGN;
}
/**
* Is this node a call expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is CALL
*/
static boolean isExprCall(Node n) {
return n.getType() == Token.EXPR_RESULT
&& n.getFirstChild().getType() == Token.CALL;
}
/**
* @return Whether the node represents a FOR-IN loop.
*/
static boolean isForIn(Node n) {
return n.getType() == Token.FOR
&& n.getChildCount() == 3;
}
/**
* Determines whether the given node is a FOR, DO, or WHILE node.
*/
static boolean isLoopStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
return true;
default:
return false;
}
}
/**
* @param n The node to inspect.
* @return If the node, is a FOR, WHILE, or DO, it returns the node for
* the code BLOCK, null otherwise.
*/
static Node getLoopCodeBlock(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.WHILE:
return n.getLastChild();
case Token.DO:
return n.getFirstChild();
default:
return null;
}
}
/**
* Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node.
*/
static boolean isControlStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
case Token.WITH:
case Token.IF:
case Token.LABEL:
case Token.TRY:
case Token.CATCH:
case Token.SWITCH:
case Token.CASE:
case Token.DEFAULT:
return true;
default:
return false;
}
}
/**
* Determines whether the given node is code node for FOR, DO,
* WHILE, WITH, or IF node.
*/
static boolean isControlStructureCodeBlock(
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Node parent, Node n) {
switch (parent.getType()) {
case Token.FOR:
case Token.WHILE:
case Token.LABEL:
case Token.WITH:
return parent.getLastChild() == n;
case Token.DO:
return parent.getFirstChild() == n;
case Token.IF:
return parent.getFirstChild() != n;
case Token.TRY:
return parent.getFirstChild() == n || parent.getLastChild() == n;
case Token.CATCH:
return parent.getLastChild() == n;
case Token.SWITCH:
case Token.CASE:
return parent.getFirstChild() != n;
case Token.DEFAULT:
return true;
default:
Preconditions.checkState(isControlStructure(parent));
return false;
}
}
/**
* Gets the condition of an ON_TRUE / ON_FALSE CFG edge.
* @param n a node with an outgoing conditional CFG edge
* @return the condition node or null if the condition is not obviously a node
*/
static Node getConditionExpression(Node n) {
switch (n.getType()) {
case Token.IF:
case Token.WHILE:
return n.getFirstChild();
case Token.DO:
return n.getLastChild();
case Token.FOR:
switch (n.getChildCount()) {
case 3:
return null;
case 4:
return n.getFirstChild().getNext();
}
throw new IllegalArgumentException("malformed 'for' statement " + n);
case Token.CASE:
return null;
}
throw new IllegalArgumentException(n + " does not have a condition.");
}
/**
* @return Whether the node is of a type that contain other statements.
*/
static boolean isStatementBlock(Node n) {
return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK;
}
/**
* @return Whether the node is used as a statement.
*/
static boolean isStatement(Node n) {
Node parent = n.getParent();
// It is not possible to determine definitely if a node is a statement
// or not if it is not part of the AST. A FUNCTION node, for instance,
// is either part of an expression (as a anonymous function) or as
// a statement.
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Preconditions.checkState(parent != null);
switch (parent.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.LABEL:
return true;
default:
return false;
}
}
/** Whether the node is part of a switch statement. */
static boolean isSwitchCase(Node n) {
return n.getType() == Token.CASE || n.getType() == Token.DEFAULT;
}
/**
* @return Whether the name is a reference to a variable, function or
* function parameter (not a label or a empty anonymous function name).
*/
static boolean isReferenceName(Node n) {
return isName(n) && !n.getString().isEmpty() && !isLabelName(n);
}
/** @return Whether the node is a label name. */
static boolean isLabelName(Node n) {
if (n != null && n.getType() == Token.NAME) {
Node parent = n.getParent();
switch (parent.getType()) {
case Token.LABEL:
case Token.BREAK:
case Token.CONTINUE:
if (n == parent.getFirstChild()) {
return true;
}
}
}
return false;
}
/** Whether the child node is the FINALLY block of a try. */
static boolean isTryFinallyNode(Node parent, Node child) {
return parent.getType() == Token.TRY && parent.getChildCount() == 3
&& child == parent.getLastChild();
}
/** Safely remove children while maintaining a valid node structure. */
static void removeChild(Node parent, Node node) {
// Node parent = node.getParent();
if (isStatementBlock(parent)
|| isSwitchCase(node)
|| isTryFinallyNode(parent, node)) {
// A statement in a block can simply be removed.
parent.removeChild(node);
} else if (parent.getType() == Token.VAR) {
if (parent.hasMoreThanOneChild()) {
parent.removeChild(node);
} else {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// This would leave an empty VAR, remove the VAR itself.
removeChild(parent.getParent(), parent);
}
} else if (node.getType
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>() == Token.BLOCK) {
// Simply empty the block. This maintains source location and
// "synthetic"-ness.
node.detachChildren();
} else if (parent.getType() == Token.LABEL
&& node == parent.getLastChild()) {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// A LABEL without children can not be referred to, remove it.
removeChild(parent.getParent(), parent);
} else if (parent.getType() == Token.FOR
&& parent.getChildCount() == 4) {
// Only Token.FOR can have an Token.EMPTY other control structure
// need something for the condition. Others need to be replaced
// or the structure removed.
parent.replaceChild(node, new Node(Token.EMPTY));
} else {
throw new IllegalStateException("Invalid attempt to remove node: " +
node.toString() + " of "+ parent.toString());
}
}
/**
* Merge a block with its parent block.
* @return Whether the block was removed.
*/
static boolean tryMergeBlock(Node block) {
Preconditions.checkState(block.getType() == Token.BLOCK);
Node parent = block.getParent();
// Try to remove the block if its parent is a block/script or if its
// parent is label and it has exactly one child.
if (NodeUtil.isStatementBlock(parent)) {
Node previous = block;
while (block.hasChildren()) {
Node child = block.removeFirstChild();
parent.addChildAfter(child, previous);
previous = child;
}
parent.removeChild(block);
return true;
} else if (parent.getType() == Token.LABEL && block.hasOneChild()) {
parent.replaceChild(block, block.removeFirstChild());
return true;
} else {
return false;
}
}
/**
* Is this a CALL node?
*/
static boolean isCall(Node n) {
return n.getType() == Token.CALL;
}
/**
* Is this a FUNCTION node?
*/
static boolean isFunction(Node n) {
return n.getType() == Token.FUNCTION;
}
/**
* Return a BLOCK node for the given FUNCTION node.
*/
static Node getFunction
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Body(Node fn) {
Preconditions.checkArgument(isFunction(fn));
return fn.getLastChild();
}
/**
* Is this a THIS node?
*/
static boolean isThis(Node node) {
return node.getType() == Token.THIS;
}
/**
* Is this node or any of its children a CALL?
*/
static boolean containsCall(Node n) {
return containsType(n, Token.CALL);
}
/**
* Is this node a function declaration? A function declaration is a function
* that has a name that is added to the current scope (i.e. a function that
* is not anonymous; see {@link #isFunctionAnonymous}).
*/
static boolean isFunctionDeclaration(Node n) {
return n.getType() == Token.FUNCTION && !isFunctionAnonymous(n);
}
/**
* Is this node a hoisted function declaration? A function declaration in the
* scope root is hoisted to the top of the scope.
* See {@link #isFunctionDeclaration}).
*/
static boolean isHoistedFunctionDeclaration(Node n) {
return NodeUtil.isFunctionDeclaration(n)
&& (n.getParent().getType() == Token.SCRIPT
|| n.getParent().getParent().getType() == Token.FUNCTION);
}
/**
* Is this node an anonymous function? An anonymous function is one that has
* either no name or a name that is not added to the current scope (see
* {@link #isFunctionAnonymous}).
*/
static boolean isAnonymousFunction(Node n) {
return n.getType() == Token.FUNCTION && isFunctionAnonymous(n);
}
/**
* Is a FUNCTION node an anonymous function? An anonymous function is one that
* has either no name or a name that is not added to the current scope.
*
* <p>Some examples of anonymous functions:
* <pre>
* function () {}
* (function f() {})()
* [ function f() {} ]
* var f = function f() {};
* for (function f() {};;) {}
* </pre>
*
* <p>Some examples of functions that are <em>not</em> anonymous:
* <pre>
* function f() {}
* if (x); else function f() {}
* for (;;) { function f() {} }
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> * </pre>
*
* @param n A FUNCTION node
* @return Whether n is an anonymous function
*/
static boolean isFunctionAnonymous(Node n) {
return !isStatement(n);
}
/**
* Determines if a function takes a variable number of arguments by
* looking for references to the "arguments" var_args object.
*/
static boolean isVarArgsFunction(Node function) {
Preconditions.checkArgument(isFunction(function));
return NodeUtil.isNameReferenced(
function.getLastChild(),
"arguments",
Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION)));
}
/**
* @return Whether node is a call to methodName.
* a.f(...)
* a['f'](...)
*/
static boolean isObjectCallMethod(Node callNode, String methodName) {
if (callNode.getType() == Token.CALL) {
Node functionIndentifyingExpression = callNode.getFirstChild();
if (NodeUtil.isGet(functionIndentifyingExpression)) {
Node last = functionIndentifyingExpression.getLastChild();
if (last != null && last.getType() == Token.STRING) {
String propName = last.getString();
return (propName.equals(methodName));
}
}
}
return false;
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.call(...)
* x['call'](...)
*/
static boolean isFunctionObjectCall(Node callNode) {
return isObjectCallMethod(callNode, "call");
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.apply(...)
* x['apply'](...)
*/
static boolean isFunctionObjectApply(Node callNode) {
return isObjectCallMethod(callNode, "apply");
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.call(...)
* x['call'](...)
* where x is a NAME node.
*/
static boolean isSimpleFunctionObjectCall(Node callNode) {
if (isFunctionObjectCall(callNode)) {
if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) {
return true;
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(Token.FUNCTION)));
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = new Node(
Token.VAR, Node.newString(Token.NAME, nameNode.getString()));
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.getType() == Token
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return A NAME or GETPROP node
*/
static Node newQualifiedNameNode(String name, Node basisNode,
String originalName) {
Node node = newQualifiedNameNode(name, -1, -1);
setDebugInformation(node, basisNode, originalName);
return node;
}
/**
* Sets the debug information (source file info and orignal name)
* on the given node.
*
* @param node The node on which to set the debug information.
* @param basisNode The basis node from which to copy the source file info.
* @param originalName The original name of the node.
*/
static void setDebugInformation(Node node, Node basisNode,
String originalName) {
node.copyInformationFromForTree(basisNode);
node.putProp(Node.ORIGINALNAME_PROP, originalName);
}
/**
* Creates a new node representing an *existing* name, copying over the source
* location information from the basis node.
*
* @param name The name for the new NAME node.
* @param basisNode The node that represents the name as currently found in
* the AST.
*
* @return The node created.
*/
static Node newName(String name, Node basisNode) {
Node nameNode = Node.newString(Token.NAME, name);
nameNode.copyInformationFrom(basisNode);
return nameNode;
}
/**
* Creates a new node representing an *existing* name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name The name for the new NAME node.
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return The node
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> created.
*/
static Node newName(String name, Node basisNode, String originalName) {
Node nameNode = newName(name, basisNode);
nameNode.putProp(Node.ORIGINALNAME_PROP, originalName);
return nameNode;
}
/** Test if all characters in the string are in the Basic Latin (aka ASCII)
* character set - that they have UTF-16 values equal to or below 0x7f.
* This check can find which identifiers with Unicode characters need to be
* escaped in order to allow resulting files to be processed by non-Unicode
* aware UNIX tools and editors.
* *
* See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode
* for more on Basic Latin.
*
* @param s The string to be checked for ASCII-goodness.
*
* @return True if all characters in the string are in Basic Latin set.
*/
static boolean isLatin(String s) {
char LARGEST_BASIC_LATIN = 0x7f;
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c > LARGEST_BASIC_LATIN) {
return false;
}
}
return true;
}
/**
* Determines whether the given name can appear on the right side of
* the dot operator. Many properties (like reserved words) cannot.
*/
static boolean isValidPropertyName(String name) {
return TokenStream.isJSIdentifier(name) &&
!TokenStream.isKeyword(name) &&
// no Unicode escaped characters - some browsers are less tolerant
// of Unicode characters that might be valid according to the
// language spec.
// Note that by this point, unicode escapes have been converted
// to UTF-16 characters, so we're only searching for character
// values, not escapes.
NodeUtil.isLatin(name);
}
private static class VarCollector implements Visitor {
final Map<String, Node> vars = Maps.newLinkedHashMap();
public void visit(Node n) {
if (n.getType() == Token.NAME) {
Node parent = n.getParent();
if (parent != null && parent
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.getType() == Token.VAR) {
String name = n.getString();
if (!vars.containsKey(name)) {
vars.put(name, n);
}
}
}
}
}
/**
* Retrieves vars declared in the current node tree, excluding descent scopes.
*/
public static Collection<Node> getVarsDeclaredInBranch(Node root) {
VarCollector collector = new VarCollector();
visitPreOrder(
root,
collector,
Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION)));
return collector.vars.values();
}
/**
* @return {@code true} if the node an assignment to a prototype property of
* some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
if (!NodeUtil.isExprAssign(n)) {
return false;
}
return isPrototypeProperty(n.getFirstChild().getFirstChild());
}
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
if (lhsString == null) {
return false;
}
int prototypeIdx = lhsString.indexOf(".prototype.");
return prototypeIdx != -1;
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (isGetProp(cur)) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode() {
// TODO(johnlenz): Why this instead of the more common "undefined"?
return new Node(Token.VOID, Node.newNumber(0));
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
/**
* Create a VAR node containing the given name and initial value expression.
*/
static Node newVarNode(String name, Node value) {
Node nodeName = Node.newString(Token.NAME, name);
if (value != null) {
nodeName.addChildrenToBack(value);
}
Node var = new Node(Token.VAR, nodeName);
return var;
}
/**
* A predicate for matching name nodes with the specified node.
*/
private static class MatchNameNode implements Predicate<Node>{
final String name;
MatchNameNode(String name){
this.name = name;
}
public boolean apply(Node n) {
return n.getType() == Token.NAME
&& n.getString().equals(name);
}
}
/**
* A predicate for matching nodes with the specified type.
*/
static class MatchNodeType implements Predicate<Node>{
final int type;
MatchNodeType(int type){
this.type = type;
}
public boolean apply(Node n) {
return n.getType() == type;
}
}
/**
* Whether a Node type is within the node tree.
*/
static boolean isNodeTypeReferenced(Node node, int type) {
return isNodeTypeReferenced(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Whether a Node type is within the node tree.
*/
static boolean isNodeTypeReferenced(
Node node, int type, Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* Finds the number of times a type is referenced within the node tree.
*/
static int getNodeTypeReferenceCount(Node node, int type) {
return getCount(node, new MatchNodeType(type));
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node,
String name,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNameNode(name), traverseChildrenPred);
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node, String name) {
return isNameReferenced(node, name, Predicates.<Node>alwaysTrue());
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> c != null; c = c.getNext()) {
visitPostOrder(c, vistor, traverseChildrenPred);
}
}
vistor.visit(node);
}
/**
* @return Whether a TRY node has a finally block.
*/
static boolean hasFinally(Node n) {
Preconditions.checkArgument(n.getType() == Token.TRY);
return n.getChildCount() == 3;
}
/**
* @return The BLOCK node containing the CATCH node (if any)
* of a TRY.
*/
static Node getCatchBlock(Node n) {
Preconditions.checkArgument(n.getType() == Token.TRY);
return n.getFirstChild().getNext();
}
/**
* @return Whether BLOCK (from a TRY node) contains a CATCH.
* @see NodeUtil#getCatchBlock
*/
static boolean hasCatchHandler(Node n) {
Preconditions.checkArgument(n.getType() == Token.BLOCK);
return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH;
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
static Node getFnParameters(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION);
return fnNode.getFirstChild().getNext();
}
/**
* Returns true if a name node represents a constant variable.
*
* <p>Determining whether a variable is constant has three steps:
* <ol>
* <li>In CodingConventionAnnotator, any name that matches the
* {@link CodingConvention#isConstant(String)} is annotated with an
* IS_CONSTANT_NAME property.
* <li>The normalize pass renames any variable with the IS_CONSTANT_NAME
* annotation and that is initialized to a constant value with
* a variable name inlucding $$constant.
* <li>Return true here if the variable includes $$constant in its name.
* </ol>
*
* @param node A NAME or STRING node
* @return True if the variable is constant
*/
static boolean isConstantName(Node node) {
return node.getBoolean
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Prop(Node.IS_CONSTANT_NAME);
}
/**
* @param nameNode A name node
* @return The JSDocInfo for the name node
*/
static JSDocInfo getInfoForNameNode(Node nameNode) {
JSDocInfo info = null;
Node parent = null;
if (nameNode != null) {
info = nameNode.getJSDocInfo();
parent = nameNode.getParent();
}
if (info == null && parent != null &&
((parent.getType() == Token.VAR && parent.hasOneChild()) ||
parent.getType() == Token.FUNCTION)) {
info = parent.getJSDocInfo();
}
return info;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
static String getSourceName(Node n) {
String sourceName = null;
while (sourceName == null && n != null) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
n = n.getParent();
}
return sourceName;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Construct a tracer whose type is based on the short name of the object
* @param object Object to use as type name
* @param comment A comment
* @return new Tracer.
*/
static Tracer shortName(Object object, String comment) {
if (object == null) {
return new Tracer(comment);
}
return new Tracer(object.getClass().getSimpleName(), comment);
}
/**
* Converts 'v' to a string and pads it with up to 16 spaces for
* improved alignment.
* @param v The value to convert.
* @param digits_column_width The desired with of the string.
*/
private static String longToPaddedString(long v, int digits_column_width) {
int digit_width = numDigits(v);
StringBuilder sb = new StringBuilder();
appendSpaces(sb, digits_column_width - digit_width);
sb.append(v);
return sb.toString();
}
/**
* Gets the number of digits in an integer when printed in base 10. Assumes
* a positive integer.
* @param n The value.
* @return The number of digits in the string.
*/
private static int numDigits(long n) {
int i = 0;
do {
i++;
n = n / 10;
} while (n > 0);
return i;
}
/**
* Gets a string of spaces of the length specified.
* @param sb The string builder to append to.
* @param numSpaces The number of spaces in the string.
*/
@VisibleForTesting
static void appendSpaces(StringBuilder sb, int numSpaces) {
if (numSpaces > 16) {
logger.warning("Tracer.appendSpaces called with large numSpaces");
// Avoid long loop in case some bug in the caller
numSpaces = 16;
}
while (numSpaces >= 5) {
sb.append(" ");
numSpaces -= 5;
}
// We know it's less than 5 now
switch (numSpaces) {
case 1:
sb.append(" ");
break;
case 2:
sb.append(" ");
break;
case 3:
sb.append("
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Token.BLOCK &&
first.getNext().getChildCount() <= 1);
Preconditions.checkState(childCount >= 2 && childCount <= 3);
add("try");
add(first, Context.PRESERVE_BLOCK);
// second child contains the catch block, or nothing if there
// isn't a catch block
Node catchblock = first.getNext().getFirstChild();
if (catchblock != null) {
add(catchblock);
}
if (childCount == 3) {
add("finally");
add(last, Context.PRESERVE_BLOCK);
}
break;
}
case Token.CATCH:
Preconditions.checkState(childCount == 3);
if (first.getNext().getType() != Token.EMPTY) {
throw new Error("Catch conditions not suppored because I think" +
" that it may be a netscape only feature.");
}
add("catch(");
add(first);
add(")");
add(last, Context.PRESERVE_BLOCK);
break;
case Token.THROW:
Preconditions.checkState(childCount == 1);
add("throw");
add(first);
// Must have a ';' after a throw statement, otherwise safari can't
// parse this.
cc.endStatement(true);
break;
case Token.RETURN:
add("return");
if (childCount == 1) {
add(first);
} else {
Preconditions.checkState(childCount == 0);
}
cc.endStatement();
break;
case Token.VAR:
if (first != null) {
add("var ");
addList(first, false, getContextForNoInOperator(context));
}
break;
case Token.NAME:
if (first == null || first.getType() == Token.EMPTY) {
addIdentifier(n.getString());
} else {
Preconditions.checkState(childCount == 1);
addIdentifier(n.getString());
cc.addOp("=", true);
if (first.getType() == Token.COMMA) {
addExpr(first, NodeUtil.precedence(Token.ASSIGN));
} else {
// Add expression, consider nearby code at lowest level of
// precedence.
addExpr(first, 0, getContextForNoInOperator
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(context));
}
}
break;
case Token.ARRAYLIT:
add("[");
addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP));
add("]");
break;
case Token.LP:
add("(");
addList(first);
add(")");
break;
case Token.COMMA:
addList(first, false, context);
break;
case Token.NUMBER:
Preconditions.checkState(childCount == 0);
cc.addNumber(n.getDouble());
break;
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: {
// All of these unary operators are right-associative
Preconditions.checkState(childCount == 1);
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type));
break;
}
case Token.HOOK: {
Preconditions.checkState(childCount == 3);
int p = NodeUtil.precedence(type);
addLeftExpr(first, p + 1, context);
cc.addOp("?", true);
addExpr(first.getNext(), p);
cc.addOp(":", true);
addExpr(last, p);
break;
}
case Token.REGEXP:
if (first.getType() != Token.STRING ||
last.getType() != Token.STRING) {
throw new Error("Expected children to be strings");
}
String regexp = regexpEscape(first.getString(), outputCharsetEncoder);
// I only use one .add because whitespace matters
if (childCount == 2) {
add(regexp + last.getString());
} else {
Preconditions.checkState(childCount == 1);
add(regexp);
}
break;
case Token.GET_REF:
add(first);
break;
case Token.REF_SPECIAL:
Preconditions.checkState(childCount == 1);
add(first);
add(".");
add((String) n.getProp(Node.NAME_PROP));
break;
case Token.FUNCTION:
Preconditions.checkState(childCount == 3);
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> boolean funcNeedsParens = (context == Context.START_OF_EXPR);
if (funcNeedsParens) {
add("(");
}
add("function");
add(first);
add(first.getNext());
add(last, Context.PRESERVE_BLOCK);
cc.endFunction(context == Context.STATEMENT);
if (funcNeedsParens) {
add(")");
}
break;
case Token.SCRIPT:
case Token.BLOCK: {
boolean stripBlock = n.isSyntheticBlock() ||
((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2));
if (!stripBlock) {
cc.beginBlock();
}
for (Node c = first; c != null; c = c.getNext()) {
add(c, Context.STATEMENT);
// VAR doesn't include ';' since it gets used in expressions
if (c.getType() == Token.VAR) {
cc.endStatement();
}
if (c.getType() == Token.FUNCTION) {
cc.maybeLineBreak();
}
// Prefer to break lines in between top-level statements
// because top level statements are more homogeneous.
if (type == Token.SCRIPT) {
cc.notePreferredLineBreak();
}
}
if (!stripBlock) {
cc.endBlock(context == Context.STATEMENT);
}
break;
}
case Token.FOR:
if (childCount == 4) {
add("for(");
if (first.getType() == Token.VAR) {
add(first, Context.IN_FOR_INIT_CLAUSE);
} else {
addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
}
add(";");
add(first.getNext());
add(";");
add(first.getNext().getNext());
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
} else {
Preconditions.checkState(childCount == 3);
add("for(");
add(first);
add("in");
add(first.getNext());
add(")");
addNonEmptyExpression(
last, getContextForNonEmptyExpression(context), false);
}
break
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
}
if (count == 1) {
// Hack around a couple of browser bugs:
// Safari needs a block around function declarations.
// IE6/7 needs a block around DOs.
Node firstAndOnlyChild = getFirstNonEmptyChild(n);
boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks();
if (alwaysWrapInBlock ||
firstAndOnlyChild.getType() == Token.FUNCTION ||
firstAndOnlyChild.getType() == Token.DO) {
cc.beginBlock();
add(firstAndOnlyChild, Context.STATEMENT);
cc.maybeLineBreak();
cc.endBlock(context == Context.STATEMENT);
return;
} else {
// Continue with the only child.
nodeToProcess = firstAndOnlyChild;
}
}
}
if (nodeToProcess.getType() == Token.EMPTY) {
cc.endStatement(true);
} else {
add(nodeToProcess, context);
// VAR doesn't include ';' since it gets used in expressions - so any
// VAR in a statement context needs a call to endStatement() here.
if (nodeToProcess.getType() == Token.VAR) {
cc.endStatement();
}
}
}
/**
* Adds a node at the left-hand side of an expression. Unlike
* {@link #addExpr(Node,int)}, this preserves information about the context.
*
* The left side of an expression is special because in the JavaScript
* grammar, certain tokens may be parsed differently when they are at
* the beginning of a statement. For example, "{}" is parsed as a block,
* but "{'x': 'y'}" is parsed as an object literal.
*/
void addLeftExpr(Node n, int minPrecedence, Context context) {
addExpr(n, minPrecedence, context);
}
void addExpr(Node n, int minPrecedence) {
addExpr(n, minPrecedence, Context.OTHER);
}
private void addExpr(Node n, int minPrecedence, Context context) {
if ((NodeUtil.precedence(n.getType()) < minPrecedence) ||
((context == Context.IN_FOR_INIT_CLAUSE) &&
(n.getType() == Token.IN))
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
*/
public class Scope implements StaticScope<JSType> {
private final Map<String, Var> vars = new LinkedHashMap<String, Var>();
private final Scope parent;
private final Node rootNode;
/** The type of {@code this} in the current scope. */
private final ObjectType thisType;
/** Whether this is a bottom scope for the purposes of type inference. */
private final boolean isBottom;
/** Stores info about a variable */
public static class Var implements StaticSlot<JSType> {
/** name */
String name;
/** Var node */
Node nameNode;
/**
* The variable's type.
*/
private JSType type;
/**
* The variable's doc info.
*/
private JSDocInfo info = null;
/**
* Whether the variable's type has been inferred or is declared. An inferred
* type may change over time (as more code is discovered), whereas a
* declared type is a static contract that must be matched.
*/
private final boolean typeInferred;
/** Input source */
CompilerInput input;
/** Whether the variable is a define */
boolean isDefine;
/**
* The index at which the var is declared. e..g if it's 0, it's the first
* declared variable in that scope
*/
int index;
/** The enclosing scope */
Scope scope;
/**
* Creates a variable.
*
* @param inferred whether its type is inferred (as opposed to declared)
*/
private Var(boolean inferred) {
this.typeInferred = inferred;
}
/**
* Gets the name of the variable.
*/
public String getName() {
return name;
}
/**
* Gets the parent of the name node.
*/
public Node getParentNode() {
return nameNode == null ? null : nameNode.getParent();
}
/**
* Gets the scope where this variable is declared.
*/
Scope getScope() {
return scope;
}
/**
* Returns the index within the scope stack.
* e.g. function Foo(a) { var b; function c(d) { } }
* a = 0, b = 1, c = 2, d = 3
*/
int getLocalVarIndex() {
int num = index;
Scope
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> s = scope.getParent();
if (s == null) {
throw new IllegalArgumentException("Var is not local");
}
while (s.getParent() != null) {
num += s.getVarCount();
s = s.getParent();
}
return num;
}
/**
* Returns whether this is a global variable.
*/
public boolean isGlobal() {
return scope.isGlobal();
}
/**
* Returns whether this is a local variable.
*/
public boolean isLocal() {
return scope.isLocal();
}
/**
* Returns whether this is defined in an extern file.
*/
boolean isExtern() {
return input == null || input.isExtern();
}
/**
* Returns {@code true} if the variable is declared as a constant,
* based on the value reported by {@code NodeUtil}.
*/
public boolean isConst() {
return NodeUtil.isConstantName(nameNode);
}
/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotaed by {@code @define}.
*/
public boolean isDefine() {
return isDefine;
}
public Node getInitialValue() {
Node parent = getParentNode();
return parent.getType() == Token.FUNCTION ?
parent : nameNode.getFirstChild();
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isInferred()}.
*/
public JSType getType() {
return type;
}
/**
* Returns the name node that produced this variable.
*/
public Node getNameNode() {
return nameNode;
}
/**
* Gets the JSDocInfo for the variable.
*/
public JSDocInfo getJSDocInfo() {
return info;
}
/**
* Sets this variable's type.
* @throws IllegalStateException if the variable's type is not inferred
*/
void setType(JSType type) {
Preconditions.checkState(isTypeInferred());
this.type = type;
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
public boolean isTypeInferred() {
return typeInferred;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
public String getInputName() {
if (input == null)
return "<non-file>";
else
return input.getName();
}
public boolean isNoShadow() {
if (info != null && info.isNoShadow()) {
return true;
} else {
return false;
}
}
@Override public boolean equals(Object other) {
if (!(other instanceof Var)) {
return false;
}
Var otherVar = (Var) other;
return otherVar.nameNode == nameNode;
}
@Override public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Scope.Var " + name;
}
}
/**
* Creates a Scope given the parent Scope and the root node of the scope.
* @param parent The parent Scope. Cannot be null.
* @param rootNode Typically the FUNCTION node.
*/
Scope(Scope parent, Node rootNode) {
Preconditions.checkNotNull(parent);
Preconditions.checkArgument(rootNode != parent.rootNode);
this.parent = parent;
this.rootNode = rootNode;
JSType nodeType = rootNode.getJSType();
if (nodeType != null && nodeType instanceof FunctionType) {
thisType = ((FunctionType) nodeType).getTypeOfThis();
} else {
thisType = parent.thisType;
}
this.isBottom = false;
}
/**
* Creates a global Scope.
* @param rootNode Typically the global BLOCK node.
*/
Scope(Node rootNode, AbstractCompiler compiler) {
this.parent = null;
this.rootNode = rootNode;
thisType = compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS);
this.isBottom = false;
}
/**
* Creates a empty Scope (bottom of the lattice).
* @param rootNode Typically a FUNCTION node or the global BLOCK node.
* @param thisType the type of {@code this} in this scope
*/
Scope(Node rootNode, ObjectType thisType) {
this.parent = null;
this.rootNode = rootNode;
this.thisType = thisType;
this.isBottom = true;
}
/** Whether this is the bottom of the lattice. */
boolean isBottom
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>() {
return isBottom;
}
/**
* Gets the container node of the scope. This is typically the FUNCTION
* node or the global BLOCK/SCRIPT node.
*/
public Node getRootNode() {
return rootNode;
}
public Scope getParent() {
return parent;
}
Scope getGlobalScope() {
Scope result = this;
while (result.getParent() != null) {
result = result.getParent();
}
return result;
}
@Override
public StaticScope<JSType> getParentScope() {
return parent;
}
/**
* Gets the type of {@code this} in the current scope.
*/
public ObjectType getTypeOfThis() {
return thisType;
}
/**
* Declares a variable whose type is inferred.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
*/
Var declare(String name, Node nameNode, JSType type, CompilerInput input) {
return declare(name, nameNode, type, input, true);
}
/**
* Declares a variable.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
* @param inferred Whether this variable's type is inferred (as opposed
* to declared).
*/
Var declare(String name, Node nameNode,
JSType type, CompilerInput input, boolean inferred) {
Preconditions.checkState(name != null && name.length() > 0);
// Make sure that it's declared only once
Preconditions.checkState(vars.get(name) == null);
Var var = new Var(inferred);
var.name = name;
var.nameNode = nameNode;
var.type = type;
var.scope = this;
var.index = vars.size();
var.input = input;
// native variables do not have a name node.
// TODO(user): make Var abstract and have NativeVar, NormalVar.
JSDocInfo info = NodeUtil.getInfoForNameNode(nameNode);
var
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>.isDefine = info != null && info.isDefine();
var.info = info;
vars.put(name, var);
return var;
}
/**
* Undeclares a variable, to be used when the compiler optimizes out
* a variable and removes it from the scope.
*/
void undeclare(Var var) {
Preconditions.checkState(var.scope == this);
Preconditions.checkState(vars.get(var.name) == var);
vars.remove(var.name);
}
public StaticSlot<JSType> getSlot(String name) {
return getVar(name);
}
public StaticSlot<JSType> getOwnSlot(String name) {
return vars.get(name);
}
/**
* Returns the variable, may be null
*/
public Var getVar(String name) {
Var var = vars.get(name);
if (var != null) {
return var;
} else if (parent != null) { // Recurse up the parent Scope
return parent.getVar(name);
} else {
return null;
}
}
/**
* Returns true if a variable is declared.
*/
public boolean isDeclared(String name, boolean recurse) {
Scope scope = this;
if (scope.vars.containsKey(name))
return true;
if (scope.parent != null && recurse) {
return scope.parent.isDeclared(name, recurse);
}
return false;
}
/**
* Return an iterator over all of the variables declared in this scope.
*/
public Iterator<Var> getVars() {
return vars.values().iterator();
}
/**
* Returns number of variables in this scope
*/
public int getVarCount() {
return vars.size();
}
/**
* Returns whether this is the global scope.
*/
public boolean isGlobal() {
return parent == null;
}
/**
* Returns whether this is a local scope (i.e. not the global scope).
*/
public boolean isLocal() {
return !isGlobal();
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>BadModuleReference(name, ref);
}
}
}
}
}
private void reportBadModuleReference(Name name, Ref ref) {
compiler.report(
JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME,
ref.module.getName(), name.declaration.module.getName(),
name.fullName()));
}
private void reportRefToUndefinedName(Name name, Ref ref) {
// grab the highest undefined ancestor to output in the warning message.
while (name.parent != null &&
name.parent.globalSets + name.parent.localSets == 0) {
name = name.parent;
}
// If this is an annotated EXPR-GET, don't do anything.
Node parent = ref.node.getParent();
if (parent.getType() == Token.EXPR_RESULT) {
JSDocInfo info = ref.node.getJSDocInfo();
if (info != null && info.hasTypedefType()) {
return;
}
}
compiler.report(
JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING,
name.fullName()));
}
/**
* Checks whether the given name is a property, and whether that property
* must be initialized with its full qualified name.
*/
private static boolean propertyMustBeInitializedByFullName(Name name) {
// If an object literal in the global namespace is never aliased,
// then all of its properties must be defined using its full qualified
// name. This implies that its properties must all be in the global
// namespace as well.
//
// The same is not true for FUNCTION and OTHER types, because their
// implicit prototypes have properties that are not captured by the global
// namespace.
return name.parent != null && name.parent.aliasingGets == 0 &&
name.parent.type == Name.Type.OBJECTLIT;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
default:
Kit.codeBug();
}
return null;
}
private static class NumberNode extends Node {
private static final long serialVersionUID = 1L;
NumberNode(double number) {
super(Token.NUMBER);
this.number = number;
}
public NumberNode(double number, int lineno, int charno) {
super(Token.NUMBER, lineno, charno);
this.number = number;
}
@Override public double getDouble() {
return this.number;
}
@Override public void setDouble(double d) {
this.number = d;
}
@Override public boolean isEquivalentTo(Node node) {
return (node instanceof NumberNode
&& getDouble() == ((NumberNode) node).getDouble());
}
private double number;
}
private static class StringNode extends Node {
private static final long serialVersionUID = 1L;
StringNode(int type, String str) {
super(type);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
StringNode(int type, String str, int lineno, int charno) {
super(type, lineno, charno);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
/** returns the string content.
* @return non null.
*/
@Override public String getString() {
return this.str;
}
/** sets the string content.
* @param str the new value. Non null.
*/
@Override public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
@Override public boolean isEquivalentTo(Node node) {
return (node instanceof StringNode &&
this.str.equals(((StringNode) node).str));
}
/**
* If the property is not defined, this was not a quoted key. The
* QUOTED_PROP int property is only assigned to STRING tokens used as
* object lit keys.
* @return true if this was a quoted string key in an object literal.
*/
@Override public boolean isQuotedString
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>() {
return last;
}
public Node getNext() {
return next;
}
public Node getChildBefore(Node child) {
if (child == first)
return null;
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null)
throw new RuntimeException("node is not a child");
}
return n;
}
public Node getChildAtIndex(int i) {
Node n = first;
while (i > 0) {
n = n.next;
i--;
}
return n;
}
public Node getLastSibling() {
Node n = this;
while (n.next != null) {
n = n.next;
}
return n;
}
public void addChildToFront(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = first;
first = child;
if (last == null) {
last = child;
}
}
public void addChildToBack(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = null;
if (last == null) {
first = last = child;
return;
}
last.next = child;
last = child;
}
public void addChildrenToFront(Node children) {
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.parent == null);
child.parent = this;
}
Node lastSib = children.getLastSibling();
lastSib.next = first;
first = children;
if (last == null) {
last = lastSib;
}
}
public void addChildrenToBack(Node children) {
for (Node child = children; child != null; child = child.next) {
// Hmmm... IRFactory doesn't remove before calling this.
Preconditions.checkArgument(child.parent == null);
child.parent = this;
}
if (last != null) {
last.next = children;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> last = children.getLastSibling();
if (first == null) {
first = children;
}
}
/**
* Add 'child' before 'node'.
*/
public void addChildBefore(Node newChild, Node node) {
Preconditions.checkArgument(node != null,
"The existing child node of the parent should not be null.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
if (first == node) {
newChild.parent = this;
newChild.next = first;
first = newChild;
return;
}
Node prev = getChildBefore(node);
addChildAfter(newChild, prev);
}
/**
* Add 'child' after 'node'.
*/
public void addChildAfter(Node newChild, Node node) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
newChild.parent = this;
newChild.next = node.next;
node.next = newChild;
if (last == node) {
last = newChild;
}
}
/**
* Detach a child from its parent and siblings.
*/
public void removeChild(Node child) {
Node prev = getChildBefore(child);
if (prev == null)
first = first.next;
else
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
child.parent = null;
}
/**
* Detaches child from Node and replaces it with newChild.
*/
public void replaceChild(Node child, Node newChild) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(child);
newChild.next = child.next;
newChild.parent = this;
if (child ==
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> first) {
first = newChild;
} else {
Node prev = getChildBefore(child);
prev.next = newChild;
}
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
public void replaceChildAfter(Node prevChild, Node newChild) {
Preconditions.checkArgument(prevChild.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(prevChild);
Node child = prevChild.next;
newChild.next = child.next;
newChild.parent = this;
prevChild.next = newChild;
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
private PropListItem lookupProperty(int propType)
{
PropListItem x = propListHead;
while (x != null && propType != x.type) {
x = x.next;
}
return x;
}
private PropListItem ensureProperty(int propType)
{
PropListItem item = lookupProperty(propType);
if (item == null) {
item = new PropListItem();
item.type = propType;
item.next = propListHead;
propListHead = item;
}
return item;
}
public void removeProp(int propType)
{
PropListItem x = propListHead;
if (x != null) {
PropListItem prev = null;
while (x.type != propType) {
prev = x;
x = x.next;
if (x == null) { return; }
}
if (prev == null) {
propListHead = x.next;
} else {
prev.next = x.next;
}
}
}
public Object getProp(int propType)
{
PropListItem item = lookupProperty(propType);
if (item == null) { return null; }
return item.objectValue;
}
public
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> boolean getBooleanProp(int propType) {
return getIntProp(propType, 0) != 0;
}
public int getIntProp(int propType, int defaultValue)
{
PropListItem item = lookupProperty(propType);
if (item == null) { return defaultValue; }
return item.intValue;
}
public int getExistingIntProp(int propType)
{
PropListItem item = lookupProperty(propType);
if (item == null) { Kit.codeBug(); }
return item.intValue;
}
public void putProp(int propType, Object prop)
{
if (prop == null) {
removeProp(propType);
} else {
PropListItem item = ensureProperty(propType);
item.objectValue = prop;
}
}
public void putBooleanProp(int propType, boolean prop) {
putIntProp(propType, prop ? 1 : 0);
}
public void putIntProp(int propType, int prop)
{
PropListItem item = ensureProperty(propType);
item.intValue = prop;
}
// Gets all the property types, in sorted order.
private int[] getSortedPropTypes() {
int count = 0;
for (PropListItem x = propListHead; x != null; x = x.next) {
count++;
}
int[] keys = new int[count];
for (PropListItem x = propListHead; x != null; x = x.next) {
count--;
keys[count] = x.type;
}
Arrays.sort(keys);
return keys;
}
public int getLineno() {
return extractLineno(sourcePosition);
}
public int getCharno() {
return extractCharno(sourcePosition);
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(
this + " is not a number node");
}
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public void set
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Double(double s) throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(
this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(
this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public void setString(String s) throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(
this + " is not a string node");
}
}
@Override public String toString()
{
return toString(true, true, true);
}
public String toString(
boolean printSource,
boolean printAnnotations,
boolean printType)
{
if (Token.printTrees) {
StringBuilder sb = new StringBuilder();
toString(sb, printSource, printAnnotations, printType);
return sb.toString();
}
return String.valueOf(type);
}
private void toString(
StringBuilder sb,
boolean printSource,
boolean printAnnotations,
boolean printType)
{
if (Token.printTrees) {
sb.append(Token.name(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else if (type == Token.FUNCTION) {
sb.append(' ');
sb.append(first.getString());
} else if (this instanceof ScriptOrFnNode) {
ScriptOrFnNode sof = (ScriptOrFnNode)this;
if (this instanceof FunctionNode) {
FunctionNode fn = (FunctionNode)this;
sb.append(' ');
sb.append(fn.getFunctionName());
}
if (printSource) {
sb.append(" [source name: ");
sb.append(sof.getSourceName());
sb.append("] [encoded source length: ");
sb.append
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>(sof.getEncodedSourceEnd()
- sof.getEncodedSourceStart());
sb.append("] [base line: ");
sb.append(sof.getBaseLineno());
sb.append("] [end line: ");
sb.append(sof.getEndLineno());
sb.append(']');
}
} else if (type == Token.NUMBER) {
sb.append(' ');
sb.append(getDouble());
}
if (printSource) {
int lineno = getLineno();
if (lineno != -1) {
sb.append(' ');
sb.append(lineno);
}
}
if (printAnnotations) {
int[] keys = getSortedPropTypes();
for (int i = 0; i < keys.length; i++) {
int type = keys[i];
PropListItem x = lookupProperty(type);
sb.append(" [");
sb.append(propToString(type));
sb.append(": ");
String value;
switch (type) {
case TARGETBLOCK_PROP : // can't add this as it recurses
value = "target block property";
break;
case LOCAL_BLOCK_PROP : // can't add this as it is dull
value = "last local block";
break;
case ISNUMBER_PROP:
switch (x.intValue) {
case BOTH:
value = "both";
break;
case RIGHT:
value = "right";
break;
case LEFT:
value = "left";
break;
default:
throw Kit.codeBug();
}
break;
case SPECIALCALL_PROP:
switch (x.intValue) {
case SPECIALCALL_EVAL:
value = "eval";
break;
case SPECIALCALL_WITH:
value = "with";
break;
default:
// NON_SPECIALCALL should not be stored
throw Kit.codeBug();
}
break;
default :
Object obj = x.objectValue;
if (obj != null) {
value = obj.toString();
} else {
value = String.valueOf(x.intValue);
}
break;
}
sb.append(value);
sb.append(']');
}
}
if (printType) {
if (jsType != null) {
String jsTypeString =
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> MAX_COLUMN_NUMBER.
*/
public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1;
/**
* COLUMN_MASK stores a value where bits storing the column number
* are set, and bits storing the line are not set. It's handy for
* separating column number from line number.
*/
public static final int COLUMN_MASK = MAX_COLUMN_NUMBER;
/**
* Source position of this node. The position is encoded with the
* column number in the low 12 bits of the integer, and the line
* number in the rest. Create some handy constants so we can change this
* size if we want.
*/
private int sourcePosition;
private JSType jsType;
private Node parent;
//==========================================================================
// Source position management
public void setLineno(int lineno) {
int charno = getCharno();
if (charno == -1) {
charno = 0;
}
sourcePosition = mergeLineCharNo(lineno, charno);
}
public void setCharno(int charno) {
sourcePosition = mergeLineCharNo(getLineno(), charno);
}
/**
* Merges the line number and character number in one integer. The Character
* number takes the first 12 bits and the line number takes the rest. If
* the character number is greater than <code>2<sup>12</sup>-1</code> it is
* adjusted to <code>2<sup>12</sup>-1</code>.
*/
protected static int mergeLineCharNo(int lineno, int charno) {
if (lineno < 0 || charno < 0) {
return -1;
} else if ((charno & ~COLUMN_MASK) != 0) {
return lineno << COLUMN_BITS | COLUMN_MASK;
} else {
return lineno << COLUMN_BITS | (charno & COLUMN_MASK);
}
}
/**
* Extracts the line number and character number from a merged line char
* number (see {@link #mergeLineCharNo(int, int)}).
*/
protected static int extractLineno(int lineCharNo) {
if (lineCharNo == -1) {
return -1;
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> Node.children is in for
// loops, this branch is extremely unlikely.
return (new SiblingNodeIterable(start)).iterator();
}
}
public boolean hasNext() {
return current != null;
}
public Node next() {
if (current == null) {
throw new NoSuchElementException();
}
try {
return current;
} finally {
current = current.getNext();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
//==========================================================================
// Accessors
public Node getParent() {
return parent;
}
/**
* Gets the ancestor node relative to this.
* @param level 0 = this, 1 = the parent, etc.
*/
public Node getAncestor(int level) {
Preconditions.checkArgument(level >= 0);
Node node = this;
while(node != null && level-- > 0) {
node = node.getParent();
}
return node;
}
/**
* Iterates all of the node's ancestors excluding itself.
*/
public AncestorIterable getAncestors() {
return new AncestorIterable(this.getParent());
}
/**
* Iterator to go up the ancestor tree.
*/
public static class AncestorIterable implements Iterable<Node> {
private Node cur;
/**
* @param cur The node to start.
*/
AncestorIterable(Node cur) {
this.cur = cur;
}
public Iterator<Node> iterator() {
return new Iterator<Node>() {
public boolean hasNext() {
return cur != null;
}
public Node next() {
if (!hasNext()) throw new NoSuchElementException();
Node n = cur;
cur = cur.getParent();
return n;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
/**
* Check for one child more efficiently than by iterating over all the
* children as is done with Node.getChildCount().
* @return Whether the node has exactly one child.
*/
public boolean hasOneChild() {
return first != null && first == last;
}
/**
* Check for more than one child more efficiently than by iterating over all
* the children as is done with Node.getChildCount().
* @return Whether the node more than
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>);
if (res != null) {
return res;
}
}
return res;
}
/**
* Checks if the subtree under this node is the same as another subtree
* including types. Returns null if it's equal, or a message describing the
* differences.
*/
public boolean checkTreeTypeAwareEqualsSilent(Node node2) {
return checkTreeTypeAwareEqualsImpl(node2) == null;
}
/**
* Compare this node to node2 recursively and return the first pair
* of nodes that differs doing a preorder depth-first traversal.
* Package private for testing. Returns null if the nodes are equivalent.
*/
NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) {
boolean eq = false;
if (type == node2.getType() &&
getChildCount() == node2.getChildCount() &&
getClass() == node2.getClass() &&
Objects.equal(jsType, node2.getJSType())) {
eq = this.isEquivalentTo(node2);
}
if (!eq) {
return new NodeMismatch(this, node2);
}
NodeMismatch res = null;
Node n, n2;
for (n = first, n2 = node2.first;
res == null && n != null;
n = n.next, n2 = n2.next) {
res = n.checkTreeTypeAwareEqualsImpl(n2);
if (res != null) {
return res;
}
}
return res;
}
public static String tokenToName(int token) {
switch (token) {
case Token.ERROR: return "error";
case Token.EOF: return "eof";
case Token.EOL: return "eol";
case Token.ENTERWITH: return "enterwith";
case Token.LEAVEWITH: return "leavewith";
case Token.RETURN: return "return";
case Token.GOTO: return "goto";
case Token.IFEQ: return "ifeq";
case Token.IFNE: return "ifne";
case Token.SETNAME: return "setname";
case Token.BITOR: return "bitor";
case Token.BITXOR: return "bitxor";
case Token.BIT
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>NAME: return "typeofname";
case Token.THISFN: return "thisfn";
case Token.SEMI: return "semi";
case Token.LB: return "lb";
case Token.RB: return "rb";
case Token.LC: return "lc";
case Token.RC: return "rc";
case Token.LP: return "lp";
case Token.RP: return "rp";
case Token.COMMA: return "comma";
case Token.ASSIGN: return "assign";
case Token.ASSIGN_BITOR: return "assign_bitor";
case Token.ASSIGN_BITXOR: return "assign_bitxor";
case Token.ASSIGN_BITAND: return "assign_bitand";
case Token.ASSIGN_LSH: return "assign_lsh";
case Token.ASSIGN_RSH: return "assign_rsh";
case Token.ASSIGN_URSH: return "assign_ursh";
case Token.ASSIGN_ADD: return "assign_add";
case Token.ASSIGN_SUB: return "assign_sub";
case Token.ASSIGN_MUL: return "assign_mul";
case Token.ASSIGN_DIV: return "assign_div";
case Token.ASSIGN_MOD: return "assign_mod";
case Token.HOOK: return "hook";
case Token.COLON: return "colon";
case Token.OR: return "or";
case Token.AND: return "and";
case Token.INC: return "inc";
case Token.DEC: return "dec";
case Token.DOT: return "dot";
case Token.FUNCTION: return "function";
case Token.EXPORT: return "export";
case Token.IMPORT: return "import";
case Token.IF: return "if";
case Token.ELSE: return "else";
case Token.SWITCH: return "switch";
case Token.CASE: return "case";
case Token.DEFAULT: return "default";
case Token.WHILE: return "while";
case Token.DO: return "do";
case Token.FOR: return "for";
case Token.BREAK: return "break";
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
case Token.CONTINUE: return "continue";
case Token.VAR: return "var";
case Token.WITH: return "with";
case Token.CATCH: return "catch";
case Token.FINALLY: return "finally";
case Token.RESERVED: return "reserved";
case Token.NOT: return "not";
case Token.VOID: return "void";
case Token.BLOCK: return "block";
case Token.ARRAYLIT: return "arraylit";
case Token.OBJECTLIT: return "objectlit";
case Token.LABEL: return "label";
case Token.TARGET: return "target";
case Token.LOOP: return "loop";
case Token.EXPR_VOID: return "expr_void";
case Token.EXPR_RESULT: return "expr_result";
case Token.JSR: return "jsr";
case Token.SCRIPT: return "script";
case Token.EMPTY: return "empty";
case Token.GET_REF: return "get_ref";
case Token.REF_SPECIAL: return "ref_special";
}
return "<unknown="+token+">";
}
/** Returns true if this node is equivalent semantically to another */
public boolean isEquivalentTo(Node node) {
if (type == Token.ARRAYLIT) {
try {
int[] indices1 = (int[])getProp(Node.SKIP_INDEXES_PROP);
int[] indices2 = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
if (indices1 == null) {
if (indices2 != null)
return false;
} else if (indices2 == null) {
return false;
} else if (indices1.length != indices2.length) {
return false;
} else {
for (int i = 0; i < indices1.length; i++) {
if (indices1[i] != indices2[i])
return false;
}
}
} catch (Exception e) {
return false;
}
} else if (type == Token.INC ||
type == Token.DEC) {
int post1 = this.getIntProp(INCRDECR_PROP, 0);
int post2 =
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>:
case Token.ELSE:
case Token.SWITCH:
case Token.WHILE:
case Token.DO:
case Token.FOR:
case Token.BREAK:
case Token.CONTINUE:
case Token.VAR:
case Token.CONST:
case Token.WITH:
case Token.CATCH:
case Token.FINALLY:
case Token.BLOCK:
case Token.LABEL:
case Token.TARGET:
case Token.LOOP:
case Token.JSR:
case Token.SETPROP_OP:
case Token.SETELEM_OP:
case Token.LOCAL_BLOCK:
case Token.SET_REF_OP:
return true;
default:
return false;
}
}
/**
* This function takes a set of GETPROP nodes and produces a string that is
* each property separated by dots. If the node ultimately under the left
* sub-tree is not a simple name, this is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
return getString();
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such
* as <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName() {
switch (getType()) {
case Token.NAME:
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name
* without a "this" reference, such as <code>a.b.c</code>, but not
* <code>this.a</code>.
*/
public boolean isUn
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>scopedQualifiedName() {
switch (getType()) {
case Token.NAME:
return true;
case Token.GETPROP:
return getFirstChild().isUnscopedQualifiedName();
default:
return false;
}
}
//==========================================================================
// Mutators
/**
* Removes this node from its parent. Equivalent to:
* node.getParent().removeChild();
*/
public Node detachFromParent() {
Preconditions.checkState(parent != null);
parent.removeChild(this);
return this;
}
/**
* Removes the first child of Node. Equivalent to:
* node.removeChild(node.getFirstChild());
* @return The removed Node.
*/
public Node removeFirstChild() {
Node child = first;
if (child != null) {
removeChild(child);
}
return child;
}
/**
* @return A Node that is the head of the list of children.
*/
public Node removeChildren() {
Node children = first;
for (Node child = first; child != null; child = child.getNext()) {
child.parent = null;
}
first = null;
last = null;
return children;
}
/**
* Removes all children from this node and isolates the children from each
* other.
*/
public void detachChildren() {
for (Node child = first; child != null; ) {
Node nextChild = child.getNext();
child.parent = null;
child.next = null;
child = nextChild;
}
first = null;
last = null;
}
public Node removeChildAfter(Node prev) {
Preconditions.checkArgument(prev.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(prev.next != null,
"no next sibling.");
Node child = prev.next;
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
child.parent = null;
return child;
}
/**
* @return A detached clone of the Node, specifically excluding its
* children.
*/
public Node cloneNode() {
Node result;
try {
result = (Node) super.clone();
result.next = null;
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> result.first = null;
result.last = null;
result.parent = null;
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
/**
* @return A detached clone of the Node and all its children.
*/
public Node cloneTree() {
Node result = cloneNode();
for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) {
Node n2clone = n2.cloneTree();
n2clone.parent = result;
if (result.last != null) {
result.last.next = n2clone;
}
if (result.first == null) {
result.first = n2clone;
}
result.last = n2clone;
}
return result;
}
/**
* Copies source file and name information from the other
* node given to the current node. Used for maintaining
* debug information across node append and remove operations.
*/
public void copyInformationFrom(Node other) {
if (getProp(ORIGINALNAME_PROP) == null) {
putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP));
}
if (getProp(SOURCEFILE_PROP) == null) {
putProp(SOURCEFILE_PROP, other.getProp(SOURCEFILE_PROP));
sourcePosition = other.sourcePosition;
}
}
/**
* Copies source file and name information from the other node to the
* entire tree rooted at this node.
*/
public void copyInformationFromForTree(Node other) {
copyInformationFrom(other);
for (Node child = getFirstChild();
child != null; child = child.getNext()) {
child.copyInformationFromForTree(other);
}
}
//==========================================================================
// Custom annotations
public JSType getJSType() {
return jsType;
}
public void setJSType(JSType jsType) {
this.jsType = jsType;
}
public FileLevelJsDocBuilder getJsDocBuilderForNode() {
return new FileLevelJsDocBuilder();
}
/**
* An inner class that provides back-door access to the license
* property of the JSDocInfo
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
builder.addProperty(fieldName, fieldType);
}
return builder.build();
}
/**
* Creates a JSType from the nodes representing a type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createFromTypeNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
switch (n.getType()) {
case Token.LC: // Record type.
return createRecordTypeFromNodes(n.getFirstChild(), sourceName, scope);
case Token.BANG: // Not nullable
return createFromTypeNodes(n.getFirstChild(), sourceName, scope)
.restrictByNotNullOrUndefined();
case Token.QMARK: // Nullable
return createNullableType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.EQUALS: // Optional
return createOptionalType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.ELLIPSIS: // Var args
return createOptionalType(
createFromTypeNodes(n.getFirstChild(), sourceName, scope));
case Token.STAR: // The AllType
return getNativeType(ALL_TYPE);
case Token.LB: // Array type
// TODO(nicksantos): Enforce membership restrictions on the Array.
return getNativeType(ARRAY_TYPE);
case Token.PIPE: // Union type
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
builder.addAlternate(createFromTypeNodes(child, sourceName, scope));
}
return builder.build();
case Token.EMPTY: // When the return value of a function is not specified
return getNativeType(UNKNOWN_TYPE);
case Token.VOID: // Only allowed in the return value of a function.
return getNativeType(VOID_TYPE);
case Token.STRING:
JSType namedType = getType(scope, n.getString(), sourceName,
n.getLineno(), n.getCharno());
if ((namedType
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> instanceof ObjectType) &&
!(enumTypeNames.contains(n.getString()))) {
Node typeList = n.getFirstChild();
if (typeList != null &&
("Array".equals(n.getString()) ||
"Object".equals(n.getString()))) {
JSType parameterType =
createFromTypeNodes(
typeList.getLastChild(), sourceName, scope);
namedType = new ParameterizedType(
this, (ObjectType) namedType, parameterType);
if (typeList.hasMoreThanOneChild()) {
JSType indexType =
createFromTypeNodes(
typeList.getFirstChild(), sourceName, scope);
namedType = new IndexedType(
this, (ObjectType) namedType, indexType);
}
}
return createNullableType(namedType);
} else {
return namedType;
}
case Token.FUNCTION:
ObjectType thisType = null;
Node current = n.getFirstChild();
if (current.getType() == Token.THIS) {
Node thisNode = current.getFirstChild();
thisType =
ObjectType.cast(
createFromTypeNodes(thisNode, sourceName, scope)
.restrictByNotNullOrUndefined());
if (thisType == null) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.thisnotobject"),
sourceName, thisNode.getLineno(), "", thisNode.getCharno());
}
current = current.getNext();
}
FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this);
if (current.getType() == Token.LP) {
Node args = current.getFirstChild();
for (Node arg = current.getFirstChild(); arg != null;
arg = arg.getNext()) {
if (arg.getType() == Token.ELLIPSIS) {
if (arg.getChildCount() == 0) {
paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE));
} else {
paramBuilder.addVarArgs(
createFromTypeNodes(
arg.getFirstChild(), sourceName, scope));
}
} else {
JSType type = createFromTypeNodes(arg, sourceName, scope);
if (arg.getType() == Token.EQUALS) {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"),
sourceName, arg.getLineno(), "", arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType = createFromTypeNodes(current, sourceName, scope);
return new FunctionType(this, null, null, paramBuilder.build(),
returnType, thisType, null);
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Sets the template type name.
*/
public void setTemplateTypeName(String name) {
templateTypeName = name;
templateType = new TemplateType(this, name);
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeName() {
templateTypeName = null;
templateType = null;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
*
*/
final class ArrowType extends JSType {
private static final long serialVersionUID = 1L;
final Node parameters;
JSType returnType;
ArrowType(JSTypeRegistry registry, Node parameters,
JSType returnType) {
super(registry);
this.parameters = parameters;
this.returnType = returnType;
}
@Override
public boolean isSubtype(JSType other) {
if (!(other instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) other;
// this.returnType <: that.returnType (covariant)
// If the return type is null, this is equivalent to unknown so we do not
// base our decision on that.
if (this.returnType != null &&
that.returnType != null &&
!this.returnType.isSubtype(that.returnType)) {
return false;
}
// that.paramType[i] <: this.paramType[i] (contravariant)
// TODO(nicksantos): This is incorrect. It should be invariant.
// Follow up with closure team on how to fix this without everyone
// hating on us.
//
// If the parameter list is null, this is equivalent of ?... so we do not
// base our decision on that.
if (this.parameters != null && that.parameters != null) {
Node thisParam = parameters.getFirstChild();
Node thatParam = that.parameters.getFirstChild();
while (thisParam != null && thatParam != null) {
JSType thisParamType = thisParam.getJSType();
if (thisParamType != null) {
JSType thatParamType = thatParam.getJSType();
if (thatParamType == null ||
!thatParamType.isSubtype(thisParamType)) {
return false;
}
}
boolean thisIsVarArgs = thisParam.isVarArgs();
boolean thatIsVarArgs = thatParam.isVarArgs();
// don't advance if we have variable arguments
if (!thisIsVarArgs) {
thisParam = thisParam.getNext();
}
if (!thatIsVarArgs) {
thatParam = thatParam.getNext();
}
// both var_args indicates the end
if (thisIsVarArgs && thatIsVarArgs) {
thisParam = null;
that
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>Param = null;
}
}
// Right now, the parser's type system doesn't have a good way
// to model optional arguments.
//
// Suppose we have
// function f(number, number) {}
// function g(number) {}
// If the second arg of f is optional, then f is a subtype of g,
// but g is not a subtype of f.
// If the second arg of f is required, then g is a subtype of f,
// but f is not a subtype of g.
//
// Until we model optional params, let's just punt on this.
// If one type has more arguments than the other, we won't check them.
//
// NOTE(nicksantos): This is described in Draft 2 of the ES4 spec,
// Section 3.4.6: Subtyping Function Types. It seems really
// strange but I haven't thought a lot about the implementation.
}
return true;
}
@Override
public boolean equals(Object object) {
// Please keep this method in sync with the hashCode() method below.
if (!(object instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) object;
// if both return types are specified, then they should be equal
if (returnType == null) {
if (that.returnType != null) {
return false;
}
} else {
if (that.returnType == null) {
return false;
}
if (!returnType.equals(that.returnType)) {
return false;
}
}
// if both types include parameters, the lists should be the same
if (parameters == null) {
return that.parameters == null;
} else if (that.parameters == null) {
return false;
}
Node thisParam = parameters.getFirstChild();
Node otherParam = that.parameters.getFirstChild();
while (thisParam != null && otherParam != null) {
JSType thisParamType = thisParam.getJSType();
JSType otherParamType = otherParam.getJSType();
if (thisParamType != null) {
// Both parameter lists give a type for this param, it should be equal
if (otherParamType != null &&
!thisParamType.equals(otherParamType)) {
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>
return false;
}
} else {
if (otherParamType != null) {
return false;
}
}
thisParam = thisParam.getNext();
otherParam = otherParam.getNext();
}
// One of the parameters is null, so the types are only equal if both
// parameter lists are null (they are equal).
return thisParam == otherParam;
}
@Override
public int hashCode() {
int hashCode = 0;
if (returnType != null) {
hashCode += returnType.hashCode();
}
if (parameters != null) {
Node param = parameters.getFirstChild();
while (param != null) {
JSType paramType = param.getJSType();
if (paramType != null) {
hashCode += paramType.hashCode();
}
param = param.getNext();
}
}
return hashCode;
}
@Override
public JSType getLeastSupertype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public JSType getGreatestSubtype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public TernaryValue testForEquality(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public <T> T visit(Visitor<T> visitor) {
throw new UnsupportedOperationException();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
returnType = safeResolve(returnType, t, scope);
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
paramNode.setJSType(paramNode.getJSType().resolve(t, scope));
}
}
return this;
}
}
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Roger Lawrence
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
public class FunctionNode extends ScriptOrFnNode {
private static final long serialVersionUID = 1L;
public FunctionNode(String name) {
super(Token.FUNCTION);
functionName = name;
}
public FunctionNode(String name, int lineno, int charno) {
super(Token.FUNCTION, lineno, charno);
functionName = name;
}
public String getFunctionName() {
return
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> null} to indicate
* that the return type is unknown.
* @param typeOfThis The type of {@code this} in non-constructors. May be
* {@code null} to indicate that the type of {@code this} is unknown.
* @param templateTypeName The template type name or {@code null}.
*/
public FunctionType(JSTypeRegistry registry, String name, Node source,
Node parameters, JSType returnType, ObjectType typeOfThis,
String templateTypeName) {
this(registry, name, source, parameters, returnType, typeOfThis,
templateTypeName, false, false);
}
/** Creates an instance for a function that might be a constructor. */
FunctionType(JSTypeRegistry registry, String name, Node source,
Node parameters, JSType returnType, ObjectType typeOfThis,
String templateTypeName, boolean isConstructor, boolean nativeType) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
nativeType);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
this.source = source;
this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY;
if (isConstructor) {
this.typeOfThis = typeOfThis != null && typeOfThis.isNoObjectType() ?
typeOfThis : new InstanceObjectType(registry, this, nativeType);
} else {
this.typeOfThis = typeOfThis != null ?
typeOfThis :
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
}
// The call type should be set up last because we are calling getReturnType,
// which may be overloaded and depend on other properties being set.
this.call = new ArrowType(registry, parameters,
(returnType == null ? getReturnType() : returnType));
this.templateTypeName = templateTypeName;
}
/** Creates an instance for a function that is an interface. */
FunctionType(JSTypeRegistry registry, String name, Node source) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE));
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkArgument(name != null);
this.source = source;
this.call = null;
this
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> implemented directly by a class or its superclass. */
public Iterable<ObjectType> getImplementedInterfaces() {
FunctionType superCtor = isConstructor() ?
getSuperClassConstructor() : null;
if (superCtor == null) {
return implementedInterfaces;
} else {
return Iterables.concat(
implementedInterfaces, superCtor.getImplementedInterfaces());
}
}
public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) {
// Records this type for each implemented interface.
for (ObjectType type : implementedInterfaces) {
registry.registerTypeImplementingInterface(this, type);
}
this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces);
}
@Override
public boolean hasProperty(String name) {
return super.hasProperty(name) || "prototype".equals(name);
}
@Override
public boolean hasOwnProperty(String name) {
return super.hasOwnProperty(name) || "prototype".equals(name);
}
@Override
public JSType getPropertyType(String name) {
if ("prototype".equals(name)) {
return getPrototype();
} else {
if (!hasOwnProperty(name)) {
if ("call".equals(name)) {
// Define the "call" function lazily.
Node params = getParametersNode();
if (params == null) {
// If there's no params array, don't do any type-checking
// in this CALL function.
defineDeclaredProperty(name,
new FunctionType(registry, null, null,
null, getReturnType()),
false);
} else {
params = params.cloneTree();
Node thisTypeNode = Node.newString(Token.NAME, "thisType");
thisTypeNode.setJSType(
registry.createOptionalNullableType(getTypeOfThis()));
params.addChildToFront(thisTypeNode);
thisTypeNode.setOptionalArg(true);
defineDeclaredProperty(name,
new FunctionType(registry, null, null,
params, getReturnType()),
false);
}
} else if ("apply".equals(name)) {
// Define the "apply" function lazily.
FunctionParamBuilder builder = new FunctionParamBuilder(registry);
// Ecma-262 says that apply's second argument must be an Array
// or an arguments object. We don't model the arguments object
Closure, 139
<FILEB>
<CHANGES>
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
<CHANGEE>
<CHANGES>
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (!NodeUtil.isFunctionAnonymous(n)
&&!NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
<CHANGEE>
<CHANGES>
private void rewriteFunctionDeclaration(Node n) {
<CHANGEE>
<CHANGES>
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = new Node(Token.VAR, fnNameNode, n.getLineno(), n.getCharno());
var.copyInformationFrom(n);
<CHANGEE>
<CHANGES>
oldNameNode.setString("");
<CHANGEE>
<CHANGES>
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
<CHANGEE>
<CHANGES>
Var v = s.getVar(name);
<CHANGEE>
<CHANGES>
Preconditions.checkState(
v == null || v.getParentNode().getType()!= Token.CATCH);
if (v!= null && parent.getType() == Token.FUNCTION) {
if (v.getParentNode().getType() == Token.VAR) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.getType() == Token.VAR) {
<CHANGEE>
<FILEE>
<FILEB>
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
n.addChildBefore(new Node(Token.EMPTY), expr);
n.addChildAfter(new Node(Token.EMPTY), expr);
reportCodeChange("WHILE node");
}
break;
<CHANGES>
<CHANGEE>
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
<CHANGES>
<CHANGEE>
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
<CHANGES>
<CHANGEE>
// Prepare a spot for the function.
<CHANGES>
<CHANGEE>
// Prepare the function
<CHANGES>
<CHANGEE>
// Move the function
<CHANGES>
<CHANGEE>
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.LABEL) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.getType() == Token.LABEL) {
extractForInitializer(n, null, null);
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverse(root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler<SCANS> new StringBuilder(32);
b.append("function (");
int paramNum = (call == null || call.parameters == null) ?
0 : call.parameters.getChildCount();
boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType();
if (hasKnownTypeOfThis) {
b.append("this:");
b.append(typeOfThis.toString());
}
if (paramNum > 0) {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
while (p != null) {
b.append(", ");
if (p.isVarArgs()) {
appendVarArgsString(b, p.getJSType());
} else {
b.append(p.getJSType().toString());
}
p = p.getNext();
}
}
b.append(")");
if (call != null && call.returnType != null) {
b.append(": ");
b.append(call.returnType);
}
return b.toString();
}
/** Gets the string representation of a var args param. */
private void appendVarArgsString(StringBuilder builder, JSType paramType) {
if (paramType.isUnionType()) {
// Remove the optionalness from the var arg.
paramType = ((UnionType) paramType).getRestrictedUnion(
registry.getNativeType(JSTypeNative.VOID_TYPE));
}
builder.append("...[").append(paramType.toString()).append("]");
}
/**
* A function is a subtype of another if their call methods are related via
* subtyping and {@code this} is a subtype of {@code that} with regard to
* the prototype chain.
*/
@Override
public boolean isSubtype(JSType that) {
if (this.equals(that)) {
return true;
}
if (that.isFunctionType()) {
if (((FunctionType) that).isInterface()) {
// Any function can be assigned to an interface function.
return true;
}
if (this